一.字符指针
在C语言中,字符串数组可以用字符数组表示
字符串是以\0
即空字符结尾的字符数组
const char* pstr = "hello bit.";
- 提问
这里是把一个字符串放到pstr指针变量里了吗?
nonono
其实是将字符串 hello bit. 的首字符h的地址放到了pstr中。
char*str="hello"
和char str[ ]="hello"
有什么区别?
可修改性:
在
char*str="hello"
中,str指向的是只读内存区域,所以不能修改字符内容。 如,将str[0]=‘l’;可能会导致程序崩溃
sizeof操作符的结果
对char*str="hello"使用sizeof(str)将返回指针大小,通常为4或8位字节
对char str[ ]="hello"使用sizeof(str)将返回字符数组大小,即6个字节
3.思考以下代码输出结果
#include <stdio.h>
int main()
{
char str1[] = "hello ";
char str2[] = "hello ";
const char *str3 = "hello ";
const char *str4 = "hello ";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
结果为
str1 and str2 are not same
str3 and str4 are same
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当
几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化
不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
二 数组名与&数组名
数组名表示数组首元素的地址,那&数组名表示什么?
#include<stdio.h>
int main(){
int ptsr[6]={0};
printf("%p\n", ptsr);
printf("%p\n", &ptsr);
return 0;
}
结果
可以看到ptsr与&ptsr输出结果一样,难道数组名与&数组名是相同的?
#include<stdio.h>
int main(){
int ptsr[6]={0};
printf("%p\n", ptsr);
printf("%p\n", ptsr+1);
printf("%p\n", &ptsr);
printf("%p\n", &ptsr+1);
return 0;
}
结果
根据上面的代码我们发现,其实&ptsr和ptsr,虽然值是一样的,但是意义应该不一样的。
实际上: &ptsr 表示的是数组的地址,而不是数组首元素的地址。 本例中 &ptsr 的类型是: int(*)[6] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &ptsr+1 相对于 &ptsr 的差值是24.
三.数组指针的使用
1.把数组arr的地址赋值给数组指针变量p
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int (*p)[10] = &arr;
2.看代码
#include<stdio.h>
void jja1(int a[4][3],int row ,int bol){
int i=0;
for(i=0;i<row;i++){
for(int j=0;j<bol;j++)
{
printf("%d",a[i][j]);
}
printf("\n");
}
}
int main(){
int a[4][3]={1,2,3,4,5,6,7,8,9,0};
jja1(a,4,3);
return 0;
}
也可以这么写
将void jja1(int a[4][3],int row ,int bol)
改为void jja1(int (*a)[5] ,int row ,int bol)
4.数组参数
a. 一维数组传参
注意:一维数组传参时,形参既可以是数组形式,也可以是指针形式
假设有以下代码
int arr[10]={0};
int *arr2[10]={0};
test(arr);
test2(arr2);
传参方式
1.void test(int arr[ ])
因为实参只是arr,即数组首元素的地址,所以接受的时候并非整个数组传过去,因此可以省略大小,这里的arr [ ]
本质上是一个指针
2.void test(int arr[ 10])
无论是10 100 1100都可以,因为它本质上test函数接收的并非整个数组,而是数组首元素的地址,所以不会根据数据大小去创建真的数组。
3.void test(int *arr)
形参为首元素地址,固用指针接受可以
4.void test2(int *arr[ ])
同一类型指针数组接受
5.void test2(int *arr)
arr2为一个指针数组类型,里面放的是指针。arr2代表这个指针数组的首元素的地址(这一内容的理解可以去标题二二【数组名与&数组名】)。arr2这个地址,存放的是一个int 类型的指针。存放指针的地址,就是一个二级指针。因此实参本质上是一个二级指针,那形参我用一个二级指针接受当然可以。
b. 二维数组传参
注意:二维数组首元素的地址为第一行的地址,即整个一维数组的地址
假设有以下代码
int arr[3][2]={{1,2},{3,4},{4,5}};
test(arr);
二维数组传参方式
1.void test(int arr[3][2])
2.void test(int arr[ ][2])
二维数组行可以省,列不能。注意:2要和形参的数组列相同。
原因:形参二维数组的首地址,即整个一维数组的地址,因此需要知道地址结束的地方。
3.void test(int (*p)[2])
首先arr代表首元素的地址,是二维数组的第一行(即例子中的{1,2,}的地址!)
既然实参是一个一维数组的地址,所以我只能用一个数组指针类型去接收。数组指针指的是指向数组的指针,依然为一个指针,存放的是整个数组的地址。
四 一级指针传参
1.了解一级指针传参
一级指针传参,改变不了原指针的地址,尽管t++,改变了地址,一旦此函数运行结果,就会被释放,不会影响到main函数的地址
可参考一下代码进行理解
int arr[3][2]={{1,2},{3,4},{4,5}};
#include<stdio.h>
void test(int *t){
t++;
printf("t=%p\n",t);
}
int main (){
int arr[4]={1,2,3,4};
int *p=arr;
test(p);//p表示一个指针变量的名字,至此指针变量指向的内存地址
printf("p=%p\n",p);
printf("%d %d %d %d\n",arr[0],arr[1],arr[2],arr[3]);
return 0;
}
2.用一级指针传参改变指针指向的内容
举例:改变arr[0]的值
int arr[3][2]={{1,2},{3,4},{4,5}};
#include<stdio.h>
void test(int *t){
*t=2;
}
int main (){
int arr[4]={1,2,3,4};
int *p=arr;
test(p);
printf("p=%p\n",p);
printf("%d %d %d %d\n",arr[0],arr[1],arr[2],arr[3]);
return 0;
}
五 二级指针传参
改变一级指针指向的内容或者一级指针地址
void test(int **t){
**t=2;
}
int main (){
int arr[4]={1,2,3,4};
int *p=arr;
test(&p);
printf("p=%p\n",p);
printf("%d %d %d %d\n",arr[0],arr[1],arr[2],arr[3]);
return 0;
}
结果:
以上是改变一级指针指向内容举例
另外
函数形参是二级指针,可以传入的参数——二级指针变量本身和一级指针变量的地址
传参方式:
#include<stdio.h>
void test(int **t){
}
int main (){
int n=1
int *p=&n;
int**pp=&p;
int*arr[10];//数组元素是指针
test(pp);//正确·
test(&p);//正确
test(arr);//正确(数组名是首元素地址,即指针地址)
return 0;
}
C语言指针笔记1 https://editor.csdn.net/md/?articleId=134108935