C编程基础day09
指针数组:的实质是数组,也就是指针的数组,每个元素都是指针。
数组指针:的实质是指针,就是指向数组的指针。
指针数组:
int a[5]={1,2,3,4,5};
int * p[5];// 指针数组:的实质是数组,也就是指针的数组,每个元素都是指针。[]的优先级比较高。
int n = sizeof(p)/sizeof(p[0]);
for(int i=0; i<n; i++)
{
p[i]=&a[i];
}
for( i=0; i<n; i++)
{
printf("%d %d \n ", *p[i], * ( *(p+i) ) ); //因为p[i]等价于*(p+i), 详见前一章分析。
}
值传递时候:变量本身传递,不管变量是什么类型,只要是变量本身值的传递,就是指传递,传递的是变量值不是变量的内存地址传递。
int swap(int m, int n)
{
int temp;
temp = m;
m = n;
n = temp;
}
int a=11, b=22;
swap(a,b); //这里是值传递。形参的修改不会影响实参。
地址传递:传递的是实参的内存地址,形参的改变会改变实参的值
int swap2(int *m, int *n)
{
int temp;
temp = *m;
*m = *n;
*n = temp;
}
int a=11, b=22;
swap2(&a,&b); //这里是地址传递。可以通过形参可以改变实参。
形参中的数组,根本不是数组,而是指针。形参中 int a[1000]、 int a[]和int *a对编译器而言没有任何区别。编译器都是当做int *来处理。 /*将形参中的数组当成指针处理的一个原因是当做指针处理效率高,把数组全考拷进去的效率肯定低于只拷贝一个数组首元素的地址效率*/
形参中的数组和非形参的数组区别: 形参中的数组是指针变量,而非形参中的数组就是真的数组,用sizeof计算时可以明显看出来。
int a=[5] ={1,2,3,4,5};
void printf_array(int a[])
{
int n = sizeof(a)/sizeof(a[0]); /*特别注意在32位编译器下n等于1,因为在32位操作系统下,编译器将int a[]当做int *处理,指针变量的大小为4字节, 同时sizeof(a[0])为第一个元素大小4字节。所以n=1。 64位操作系统下n=2. */
for(int i=0; i<n; i++)
{
printf("%d",a[i]);
}
}
int n = sizeof(a)/sizeof(a[0]);
void printf_array_2(int a[], int n) /*形参当中真要用数组时,要把数组元素个数n写进形参,不然函数不知道数组的大小,最好的例子就是main函数*/
{
for(int i=0; i<n; i++)
{
printf("%d",a[i]); //方括号要记住a[i]等价于*(a+i)
}
}
int b[10][10];
void fun(int b[10][10]); //OK
void fun(int ]**b); //err, 二维数组不是二级指针
返回值是指针类型: 返回的是一个地址,示例如下:
int * fun();
int *p=NULL;
p= fun();//接收函数返回的地址
*p=100;//,操作指针指向的内存,前提是p指向的不是野指针。
%s为什么能打印字符串,因为%s知道字符串首地址,所以从首元素地址开始打印,知道遇到结束符‘\0’就停止。
char str[100]="hello jack";
printf("%s\n",str);//OK
printf("%s\n",*str);//ERR
printf("%c\n",*str);//OK, 加*号时候打印的首字符%c.
字符指针 char *p;
char str[]="hello jack! \n";
p = str;
字符串拷贝的时候不能给野指针拷贝内容。会报段错误。
自己写字符串拷贝函数时候最后一定要在目标函数后加上结束符号‘\0’或数字0.
数字0于‘\0’等价
'0'是字符,不是结束符,ASCII码为48.
char str[] = {'a','b'};
printf("%s \n",str)//err,打印为乱码,因为找不到字符结束符‘\0’或数字0.
char str[10] = {'a','b'};
printf("%s \n",str)//OK,打印正常,因为str[10]后边会自动补0.
char str[10] = {'a','b',0};
printf("%s \n",str)//OK,打印正常
char str[10] = {'a','b',‘\0’};
printf("%s \n",str)//OK,打印正常
char str[10] = {'a','b',‘0’};
printf("%s \n",str)//err,打印乱码
char str[10] = “hello";// 以字符串初始化,会自动加一个隐藏的结束字符‘\0'.
1、每个字符串常量都是一个地址,这个地址就是首元素的地址。
2、字符串常量存储在data区(文字常量区),data区分为只读区,和可读写区。
3、变量放在栈区
4、字符串常量,文字常量区的字符串是只读的,不能修改。
5、文字常量区的生命周期和全局变量一样,程序结束才释放。
printf("s1=%s\n","hello jack!");
printf("s1=%p\n","hello jack!");
printf("s1=%s\n","hello jack!"+1);//打印结果为ello jack 因为相当于打印首元素地址+1之后的字符串.
printf("s1=%c\n", *("hello jack!"));//打印首元素
以下三处打印的地址是相同的。字符串常量是一个地址
printf("s1=%p\n", "hello mike");
char *p1 = "hello mike";
printf("p1=%p\n", p1);
char *p2 = "hello mike";
printf("p2=%p\n", p2);
printf("p1=%c\n",*p1);//OK,可以读
*p1 = 'a'; //err,会出现段错误,因为字符串常量,文字常量区的字符串是只读的,不能修改。
strcpy(p2,"abc")//err,会出现错误,这个p2虽然不是野指针,但是p2指向字符串常量,文字常量区的字符串是只读的,不能修改。
char *p =NULL;
"hello 123";
p = "hello 123";
*p='a';//err
p=NULL;//ok
p="abcdfeg";//OK, 因为P是变量,P变化指向的内存地址是可以的,但是不能改变指向内存地址的内容, 这点和strcpy注意一下区别。
另外注意:但是char 数组名, 数组名是常量不能修改。
char buf[100];
buf = "hello";//err,因为数组名是常量,不能修改指向的内容,除非一开始就指向这个内容, 或者用以下strcpy().
strcpy(buf,"hello");//OK
字符串常量初始化于字符数组初始化的区别:
1、字符串常量是把首地址赋值给指针, 指针指向的字符串常量不能修改。字符串还在字符常量区。
2、字符数组是一个接一个元素内容放入数组中,数组中的元素可以修改。 字符数组存在栈区。
char *p1="helle";
char buf[]="hello";
一下三种等价, char * *a不是二级数组。
void print_arry(char * a[100], int n);
void print_arry(char * a[], int n);//a[]是数组,每个数组元素为char *,数组每个元素都是字符地址。
void print_arry(char * *a, int n); //有些main函数也写为int main (int argc, char * argv[])或int main (int argc, char * *argv)
gcc编译时候main函数默认的参数为./a.out。printf("%s\n",argv[0]);
gcc想要向main函数传递多个参数可以在终端输入 ./a.out abcdefg hello 等参数。
平时我们使用指令cp src dst时候cp为C语言写的可执行程序, src和dst为传的参数。其他命令也是这种形式实现的
查找一个大字符串中,某个小字符串如“abcd”出现的次数可以使用strstr函数。char *temp=strstr(p,"abcd");第一次找到后,第二次从temp+4开始找.
两头堵类型经常使用。
手写字符串拷贝函数时候,最后记得加0或者'\0'
不用库函数实现atoi功能。
计算一个字符串由数字和逗号组成,计算所有数字和: 提示使用strtok()函数进行字符串切割。