C语言指针

本文深入讲解了C++中的指针,包括指针的初始化、指针与数组的关系、指针数组、数组指针、二维数组、void指针、NULL指针、指向指针的指针、常量与指针的关系,以及参数传递中的指针用法。通过实例展示了如何通过指针操作数组和如何在不同场景下使用各种类型的指针。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

指针初始化

首先,了解两个符号: ‘*’ 为取值运算符, ‘&’为取址运算符;
*p:代表取变量p的值,&a代表取变量a的地址;
初始化指针:

int *p, a = 4;  //定义一个整型指针*p,整型变量a=4
p = &a;         //取a的地址赋给p,这样p存放的即为变量a的地址
//初始化也可写为 int *p = &a 这和上面两句是等价的,当然要把定义a放在这一句前面
printf("*p is %d\n", *p); //*p为取p的值,p为a的地址,所以等同于取a的值
printf("p address is : %d\n", p);
printf("a is %d\n", a);   //即*p等于a
printf("a address is : %d\n", &a);

运行结果为:
在这里插入图片描述
可以看到p值即为a的地址,*p等于a;

指针与数组

数组名a代表数组的首地址,将地址赋给指针p,那*p就代表数组的第一个元素;

int a[5] = {1, 8, 2, 3, 5};
int *p;
p = a;
printf("a is %d\n", a);
printf("p is %d\n", p);
printf("a[0] is %d\n", a[0]);
printf("*p is %d\n", *p);
printf("*(p+1) is : %d\n", *(p+1));
printf("*(p+2) is : %d\n", *(p+2));
system("pause");
return 0;

结果如下:
在这里插入图片描述

可以看到* (p+1)代表数组从首地址偏移一个整型地址的值,* (p+n)代表数组从首地址偏移n个整型地址的值;所以可以用指针去访问数组的值;
可以看到(p+1)的值在p的值上增加了4,即偏移了一个整型地址大小;
注意若只移动一个整型地址大小,写法为*(p+1); 而* p+1代表首地址的值加1;

指针数组和数组指针

指针数组(:存放指针的数组,指针数组存放的是地址)

形如:int *p[5];
[]的优先级高于*,所以每个元素的为整型指针变量 int *这样;
如下初始化一个指针数组并打印数组的值:

int a = 1, b = 3, c = 5, d = 7, e = 9;
int *p[5] = {&a, &b, &c, &d, &e};
int i;
for(i = 0;i<5;i++)
{
    printf("a-e number is %d\n", *p[i]);
}
system("pause");
return 0;

在这里插入图片描述
在例如定义指针数组存放5个字符串:

char *p[5] = {"abc", "def", "ghi", "jkl", "mno"}; //初始化指针数组存放5个字符串的地址
int i;
for(i = 0;i < 5;i++)
{
    printf("a-e string is %s\n", p[i]);           //打印指针数组,这里因为上面存放的是字符串的地址所以不用加*取值
}
system("pause");
return 0;

在这里插入图片描述

数组指针(指向数组类型的指针,通过指针引用数组)

形如:int (*p2)[5];代表一个指向5个元素数组的指针;
如下定义一个数组指针:

int temp[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &temp;
int i;
for(i = 0;i < 5;i++)
{
    printf("*(*p+i) is %d\n", *(*p+i));
    printf("(*p)[i] is %d\n", (*p)[i]);
}

结果如下,可以看到*(*p+i)和(*p)[i]是等价的:
在这里插入图片描述

指针和二维数组

定义如下4行5列二维数组:
在这里插入图片描述
整型二维数组存储形式和存储地址如下:
在这里插入图片描述

int array[4][5] = {0};
int i, j, k = 0;
for(i=0;i<4;i++)
{
    for(j=0;j<5;j++)
    {
        array[i][j] = k++;
    }
}
printf("array is %d\n", array);                //array代表第0行第0列元素的地址
printf("&array[0][0] is %d\n", &array[0][0]);  //打印第0行第0列元素的地址
printf("*array is %d\n", *(array+0)+1);        
printf("&array[0][1] is %d\n", &array[0][1]);
printf("*(array+1) is %d\n",*(array+1));       //打印第二行第一列的地址
printf("&array[1][0] is %d\n", &array[1][0]);  //打印第二行第一列的地址
system("pause");
return 0;

结果如下:
在这里插入图片描述
看出:*(array+0)+1 == &array[0][1];
故: *(array+m)+n == &array[m][n] 为m+1行n+1列的地址;
*(array+1) == &array[1][0];
*(array+m) == &array[m][0];为第m+1行第一列的地址;
若要得到上述元素的值再进行取值操作即可如: * ( *(array+m)+n) 和 *( *(array+m))
为取第m+1行第n+1列的元素的值;

数组指针和二维数组

定义如下二维数组和数组指针:

int array[4][5] = {0};
int i, j, k = 0;
for(i=0;i<4;i++)
{
    for(j=0;j<5;j++)
    {
        array[i][j] = k++;
    }
}
int (*p)[5] = array;    //数组指针初始化
printf("**(p+1) is %d\n", *(*(p+1)+0));
printf("**(array+1) is %d\n", *(*(array+1)+0));
printf("array[1][0] is %d\n", array[1][0]);
system("pause");
return 0;

结果如下:
在这里插入图片描述
三种方式的访问结果完全相同;

void指针

void指针被称之为通用指针,可以指向任意类型的数据,也就是说,任何类型的指针都可以赋值给void指针,可以进行不同类型指针的转换;
void指针使用:

int num = 128;
int *p = &num;       //初始化整型指针
char *ps = "giao";   //初始化字符型指针
void *pv;            //定义void指针
pv = p;              //将int型指针赋给void指针
printf("p arress is: %p, pv address is: %p\n", p, pv);
//printf("*pv is %d\n", *pv);没有进行类型转换的错误写法
printf("*pv is %d\n", *(int *)pv); //类型转换成整型指针
pv = ps;             //将char型指针赋给void指针
printf("ps arress is: %p, pv address is: %p\n", ps, pv);
//printf("*pv is %s\n", pv);
printf("*pv is %s\n", (char *)pv); //类型转换成字符型
system("pause");
return 0;

运行成功结果如下:
在这里插入图片描述
可以看到int和char型的指针都可以赋值给void指针;但是,注意输出值的时候需要进行类型转换,将void型转换成需要的类型;

NULL指针

不指向任何地址的指针,不清楚将指针初始化为什么地址时,请将它初始化为NULL;

int *p1;                             //没有初始化的野指针
int *p2  = NULL;
printf("*p1 value is %d\n", *p1);    //打印p1所指向的值
//printf("*p2 value is %d\n", *p2);  //打印p2所指向的值,因为是空指针所以使用取值会报错
printf("p2 value is %d\n", p2);      //打印p2的值
system("pause");
return 0;

结果如下:
在这里插入图片描述
看到野指针随机指向一个值,而NULL指针为空地址;

指向指针的指针

套娃:
在这里插入图片描述

定义一个指向指针*p的指针 *pp

int num = 123;
int *p = &num;                       //指针*p指向num,p的值存放num的地址
int **pp = &p;						 //指针**pp指向*p,*pp的值存放*p的地址
printf("num address is %d\n", &num); 
printf("p adress is %d\n", p);
printf("*pp is %d\n", *pp);
printf("&p is %d\n", &p);
printf("pp address is %d\n", pp);
printf("num is %d\n", num);
printf("*p is %d\n", *p); 
printf("**pp is %d\n", **pp);
system("pause");
return 0;

结果如下:
在这里插入图片描述
看出pp的地址 *pp 和 * p的地址p和num的地址&num是相同的地址;
pp的值和 * p的值和num的值是相同的;

常量与指针

《1》指向常量的指针
1.指针可以修改为指向不同的常量;
2.指针可以修改为指向不同的变量;
3.可以通过解引用来读取指针指向的数据;
4.不可以通过解引用修改指针指向的数据;
《2》指向非常量的常量指针:
指针自身不可以被修改;
指针指向的值可以被修改;
《3》指向常量的常量指针:
指针自身不可以被修改;
指针指向的值也不可以被修改;

    int num = 520; 
    const int nnum = 123;          
    const int cnum = 880;
    const int *const pv = &nnum; //指向常量的常量指针
	int *const p = &num;         //指向非常量的常量指针
    const int *pc = &cnum;       //指向常量的指针
    const int *const *pvv = &pv; //指向常量的常量指针的指针
    printf("cnum is %d, cnum address is %p\n", cnum, &cnum);
    printf("*pc is %d, address pc is %p\n", *pc, pc);
    printf("*p is %d, address p is %p\n", *p, p);
    //*pv = 111;			   //试图修改指向的值会报错
    *p = 1024;                 //指向的值可以修改但自身不能修改
    //p = &cnum;   //自身不能被修改,加上这句会报错
    //*pc = 1024;  //*pc和const num一样不可被修改,加上这句会报错
    pc = &num;     //修改指针指向变量num的地址
    //pv = &num;   //试图修改自身的值也会报错
    printf("*pc is %d, address pc is %p\n", *pc, pc);
    printf("*p is %d, address p is %p\n", *pc, pc);
    num = 1000;
    printf("*pc is %d, address pc is %p\n", *pc, pc);

结果:
在这里插入图片描述

参数与指针

传值和传址:
例如:

void swap(int x,int y);
void swap1(int *x,int *y);
int main()
{
   int x = 2, y = 3;
    swap(x, y);
    printf("互换后的x: %d, y: %d\n", x, y);
    swap1(&x,&y);
    printf("互换后的x: %d, y: %d\n", x, y);
    system("pause");
    return 0;
}

void swap(int x,int y)
{
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
}
void swap1(int *x,int *y)
{
    int tmp;
    tmp = *x;
    *x = *y;
    *y = tmp;
}

swap函数使用后,因为变量的作用域原因x,y并不能交换;
swap1使用后,因为指针存储的是变量的地址,故可以交换;
可变参数传入:

int sum(int n, ...);
int sum(int n, ...)       //...表示参数个数不确定
{
    int i, sum = 0;
    va_list vap;

    va_start(vap, n);     //传入
    for(i=0;i<n;i++)
    {
        sum+=va_arg(vap, int); //获取每一个参数的值,int代表参数类型
    }
    va_end(vap);          //关闭参数列表
    return sum;
}
int main()
{
    int res;
    res = sum(3,1,5,4);   //3代表三个参数,后面为传入的值
    printf("res is %d\n", res);
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RambOoO_l

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值