宁波网站建设果核网站优化查询
int main()
{char ch = 'w';char* pc = &ch;//pc就是字符指针//const char *p = "abcdef";//这里其实是把字符串"abcdef"的首地址放入了指针p中//*p = 'w';//这是错误的无法修改值(可以看到这里绿色波浪线警告)char arr[] = "abcdef";//创建了一个arr数组,里面存了字符串char* p = arr;*p = 'w';//此时这里的首元素可以被修改printf("%s\n", arr);return 0;
}
可以发现,通过字符指针 p ,将数组arr首元素a改为了w
而创建指针,直接指向字符串,是没法修改的
因为p里面存储的是,字符串abcdef的首地址
仔细比较二者之间的区别
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";//这里字符型数组 str1[]和str2[]是分别创建了两块不同的空间,来存放相同的hello bit.内容,//这两块空间的起始地址不同,所以str1和str2代表的首元素地址也不同。const char* str3 = "hello bit.";const char* str4 = "hello bit.";//这里是创建了两个符号型指针str3和str4指向了同一块空间hello bit.的首地址//所以指针str3和str4的地址相同if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");//str1 and str2 are not sameif (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");//str3and str4 are samereturn 0;
}
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当 几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化 不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
int arr1[10]; //整形数组 - 存放整型的数组char arr2[4]; //字符数组 -存放字符的数组那么指针数组 - 存放指针的数组char* arr3[5];//
1. 指针数组举例
int main()
{//指针数组:是一个存放指针的数组。char* arr[] = { "abcdef", "hehe", "qwer" };//数组里每一个元素,都是字符串的首地址,指针int i = 0;for (i = 0; i < 3; i++){printf("%s\n", arr[i]);//arr[i] <=> *(arr+i)}return 0;
}
通过指针数组里,每个元素(首元素地址 - 指针),循环打印出数组里完整的字符串
int main()
{//创建3个一维数组int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };//arr[i] == *(arr+i)//创建arr指针数组 ,是一个存放整型指针的数组int* arr[] = { arr1, arr2, arr3 };//将3个一维数组的首元素地址(指针)存储int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){//printf("%d ", arr[i][j]);printf("%d ", *(arr[i]+j));//arr[i]遍历一维数组,arr[i][j]遍历得到一维数组里的每个元素,}printf("\n");}return 0;
}
整形指针: int * pint; 能够指向整形数据的指针。浮点型指针: float * pf; 能够指向浮点型数据的指针。那数组指针应该是:能够指向数组的指针。
//p1, p2分别是什么?
int *p1[10]; //指针数组
int (*p2)[10];//数组指针如果容易混淆,可以试着这样分辨:1)int *p1[10]; 去掉命名为 int * [10] ,可以发现是一个拥有10个元素的数组,该数组里每个元素的类型是 int * 也就是整型指针类型 ,最后结合起来就是,指针数组了2)int (*p2)[10];//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针。//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合简单来说,指针p先和谁结合,他就是什么东西:int *p1[10] 先和 [] 结合,则为数组(然后是int * ,类型为整型指针)则为指针数组;int (*p2)[10] 先和*结合 ,则为指针(然后是int [], 整型数组)则为数组指针
arr 和 &arr 有什么区别?
数组名绝大部分情况下是数组首元素的地址
但是有2个例外:
1. sizeof(数组名) - sizeof内部单独放一个数组名的时候。sizeof(arr) - 此时数组名表示的整个数组,计算得到的是数组的总大小
2. &arr - 这里的数组名表示整个数组,取出的是整个数组的地址,从地址值的角度来讲和数组首元素的地址是一样的,但是意义不一样
int main()
{int arr[10] = { 0 };printf("%d\n", sizeof(arr)); // 此时arr表示整个数组,计算得到整个数组的内存大小 - 40字节printf("%p\n", arr);//int * //arr数组名通常为首元素地址printf("%p\n", arr+1);//首元素地址 +1 ,指针往后移动一个int长度(4字节)printf("%p\n", &arr[0]);//int* //取首元素地址printf("%p\n", &arr[0]+1);//首元素地址 +1 ,指针往后移动一个int长度(4字节)printf("%p\n", &arr);//int(*)[10] //取数组地址 - 此时地址代表整个数组printf("%p\n", &arr+1);//数组地址+1,跳过整个数组(移动了40个字节)int (*p)[10] = &arr;//p是一个数组指针//int(*)[10]return 0;
}
之所以说 arr 和arr[0]是首元素地址,是因为+1之后它们都只跳过一个整型,4个字节
而&arr +1后跳过了整个数组,40个字节(看16进制末尾两个数字,68-40=28H,换算成十进制,2*16^1 + 8*16^0 = 32+8 =40 )
从而推断它们指针的类型为 :
arr 和 arr[0] - int* 整型指针(+1跳过一个整型)
&arr - int (*)[] 数组指针 (+1跳过整个数组)
2.数组指针的运用
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int (* p)[10] = &arr;//数组指针 - 指向arr整个数组int i = 0;//p --- &arr//*p --- *&arr//*p --- arr//虽然对,但是不推荐//for (i = 0; i < sz; i++)//{// printf("%d ", (*p)[i]);//*p解引用数组指针,得到arr数组,再通过下标i得到arr数组的每个元素//}//虽然对,但是不推荐//for (i = 0; i < sz; i++)//{// printf("%d ", *((*p) + i));//因为p - &arr , *p - arr,arr即首元素地址,arr[i] <=> *(arr+1)// //所以,得到*((*p)+1)//}//使用指针来访问//int* p = arr;//for (i = 0; i < sz; i++)//{// printf("%d ", *(p + i));//}//下标的形式访问数组//for (i = 0; i < sz; i++)//{// printf("%d ", arr[i]);//}return 0;
}
打印出数组的内容:
1.最简单的办法就是通过下标 和 指针
2.通过数组指针访问数组元素(不太推荐)
有点杀鸡用牛刀的感觉,没有必要。
上面说过,数组指针是一个指向数组的指针,+1就跳过整个数组。而我们仅仅是想要打印数组的每个元素而已,就用下标或者普通的整型指针就可以了。
但是也可以试试用数组指针
1)第一种写法:解引用 数组指针 得到数组,再通过下标访问元素
int(*p)[10] = &arr , p的值为数组地址&arr,*p解引用一次,*(&arr)得到数组arr,
数组arr[]用下标再去访问每个元素,实现代码
2)第二种写法:解引用 数组指针 得到数组,再通过普通整型指针访问元素
同样的
p --- &arr
*p --- *&arr
*p --- arr*p得到首元素地址,(*p)+1 地址+1,移动一个整型,再用 *((*p)+1 ) 再对整个地址解引用,得到元素
//一维数组传参,形参是数组
void print1(int arr[10], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
//一维数组传参,形参是指针
void print2(int *arr, int sz)
{int i = 0;for (i = 0; i < sz; i++){//printf("%d ", arr[i]);printf("%d ", *(arr+i));}printf("\n");
}
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int sz = sizeof(arr) / sizeof(arr[0]);print1(arr, sz);printf("\n");print2(arr, sz);return 0;
}

2.二维数组传参(形参是 二维数组 或者 数组指针)
//二维数组传参,形参是 二维数组
void print1(int arr[3][5], int r, int c)
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
//二维数组传参,形参是 数组指针 - 指向第一个一维数组的指针
void print2(int(*arr)[5], int r, int c)
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){//printf("%d ", *(*(arr + i) + j));//arr[i]printf("%d ", arr[i][j]);//arr[i]}printf("\n");}
}
int main()
{int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};//二维数组的数组名,也表示首元素的地址//二维数组的首元素是第一行//首元素的地址就是第一行的地址,是一个一维数组的地址print1(arr, 3, 5);printf("\n");print2(arr, 3, 5);return 0;
}
1)二维数组传参,形参是 二维数组
int arr[3][5]传上去的是首元素的地址,也就是第一个一维数组的地址。
arr[i]得到每一个一维数组, arr [i][j]进而得到一维数组里的每个元素,这样,二维数组里每个元素便得到了
2)二维数组传参,形参是 数组指针 - 指向第一个一维数组的指针
int(*arr)[5] 传上去的是数组指针,指向第一个一维数组的首元素地址,同理遍历得到每个元素的地址

函数指针:指向函数的指针&函数名 得到函数的地址
void test(){printf("hehe\n");}//下面pfun1和pfun2哪个有能力存放test函数的地址?void (*pfun1)();void *pfun2();pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参 数,返回值类型为void。
int Add(int x, int y)
{return x + y;
}
int main()
{//printf("%p\n", &Add);//printf("%p\n", Add);//pf就是函数指针int (* pf)(int, int) = Add;//函数的地址要存起来,就得放在【函数指针变量】中int ret = (*pf)(3, 5);//int ret = Add(3, 5);//int ret = pf(3, 5);printf("%d\n", ret);//&函数名得到就是函数的地址printf("%p\n", Add);printf("%p\n", &Add);return 0;
}
2.可以给函数指针重命名
typedef int* ptr_t;
typedef void(*pf_t)(int);//将void(*)(int)类型重新起个别名叫pf_ttypedef void(*pf_t2)(int);//pf_t2是类型名
void(*pf)(int);//pf是函数指针变量的名字
//代码1( *( void (* )( ) )0 )( );该代码是一次函数调用1. 将0强制类型转换为void (*)() 类型的函数指针
2. 这就意味着0地址处放着一个函数,函数没参数,返回类型是void
3. 调用0地址处的这个函数
//代码2void (*signal(int , void (*)(int) ) )( int );gai代码是一个函数的声明
函数的名字是signal
signal函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
该函数指针指向的函数参数是int,返回类型是void
signal函数的返回类型也是一个函数指针
该函数指针指向的函数参数是int,返回类型是void
void (* signal(int, void(*)(int)))(int)