C:一级指针与二级指针的详解

本文深入解析C语言中的一级指针与二级指针概念,通过实例展示指针的运算与应用,包括指针与函数参数的交互,以及如何通过二级指针实现函数内部指针的动态赋值。

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


指针是C语言的灵魂,我想对于一级指针大家应该都很熟悉,也经常用到:比如说对于字符串的处理,函数参数的“值,结果传递”等,对于二级指针或者多级指针,我想理解起来也是比较容易的,比如二级指针就是指向指针的指针、… 、n级指针就是…

一级指针

一级指针就是指指针,里面放的就是元素的地址,我们可以通过访问元素的地址来找到该地址里存放的内容,即元素本身。
比如:

int main(){
	char str[] = { 1, 2, 3, 4, 5, 6, 7 };
	int i;
	for (i = 0; i < sizeof(str) / sizeof(str[0]); i++){
		printf("%d", *str+i);
	}
	system("pause");
	return 0;
}

用途:

通常当作函数的输入参数,因为一级指针作为函数参数,在调用的时候,实参和形参是不同的内存空间,只是,这个内存空间存放的指针指向的是同一块地址 ,所以形参在函数执行中可以访问实参指向的内存空间,但是形参的指向的改变并不能影响实参。 总结一句话“一级指针做函数参数, 在函数内做形参做重新指向并不会影响实参的指向”

函数传参知识点:

  1. 当传入参数时,函数形参会立即申请形参的内存空间,函数执行完毕后,形参的内存空间立即释放掉。
  2. 函数参数传参本质:实参传给形参,都是拷贝

一级指针应用需要注意的:

  1. 不要在函数中,改变形参的指向来达到改变实参指向的效果,因为形参和实参只是两个指向同一空间的不同的指针。
  2. 因为形参和实参指向的是同一空间,所以可以在形参中改变其指向空间的值,如此实参指向的空间地址也发生改变。

代码实例:

#include <stdio.h>
#include <stdlib.h>

void change1(int p){
	p = 20;
}

void change2(int *p){
	*p = 20;
}

int main(){
	int a = 10;

	change1(a);
	printf("%d\n", a);
	change2(&a);
	printf("%d\n", a);

	system("pause");
	return 0;
}

代码生成图:
在这里插入图片描述
为什么change1没有修改,而change2修改了呢?

change(a)传参时,p 申请内存空间,这时有两个内存空间,a的内存空间内容10拷贝形参value,这时value内存空间的内容为10,p = 20,修改了p 内存空间的内容,a内存空间的内容仍然是10,所以未修改。

change(&a)传参时,指针变量p申请内存空间,这时有两个内存空间,a的地址拷贝给形参p,p的内存空间存放的是a的地址,即p指向a,*p = 20,即修改p指向的内存空间——a的值。所以修改了。

二级指针

二级指针就是指向一级指针的指针,里面保存的是一级指针变量的内存地址

比如:

	int a=10;
	int *pa=&a;
	int **ppa=&pa;//表示a的地址存放在pa中,pa的地址存放在ppa中,pa是一级指针,ppa是二级指针.

在以上 ppa指向 pa、pa指向 a的指向关系中:

  • a 是一段内容( 10 );
  • pa 是一个指针变量,其中存放着C的地址,但是pa也要占空间的啊,所以pa也有地址;
  • ppa 是一个二级指针变量,其中存放着 pa的地址, ppa也有地址

用途:

在函数内部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效,那么我们就需要二级指针!

#include <stdio.h>
#include <stdlib.h>

int a = 10;
int b = 100;
int *q;

void func(int **p){
	printf("func:&p = %p, p= %d\n", &q, **p);
	*p = &b;
	printf("func:&p = %p, p= %d\n", &q, **p);
}

int main(){
	printf("&a = %p, &b = %p, &q = %p\n", &a, &b, &q);
	q = &a;
	printf("*q = %d, q = %p, &q = %p\n", *q, q, &q);
	func(&q);
	printf("*q = %d, q = %p, &q = %p\n", *q, q, &q);

	system("pause");
	return 0;
}

代码生成图:
在这里插入图片描述
将指针q的地址到函数(二级指针**p),(拷贝了指针但是指针内容,也就是指针所指向的地址是不变的)所以它还是指向一级指针q(*p = q)。在这里无论拷贝多少次,它依然指向q,那么 *p = &b;自然的就是 q = &b 了。

指针运算

  • 指针 + - 整数
int main(){
    float values[5];
    float *vp;
    //指针+-整数;指针的关系运算
    for (vp = &values[0]; vp < &values[5];) {
        *vp++ = 0;     
    }
    return 0;
} 
  • 指针 - 指针

当2个指针指向同一个区域的时候,指针减去指针得到的是指针和指针之间元素的个数,而不是字节数。

int main(){
    int a[5][5];
    int (*p)[4];
    p = a;
    printf("%p, %d", &p[4][2] - &a[4][2],  &p[4][2] - &a[4][2]);
    // 0xfffffffc, -4
    return 0;
}
  • 指针的关系运算
int main() {
    int arr[10] = {1,2,3,4,5};
    int* p = arr; 
    while(p <= &arr[9]){//指针比较大小 
        printf("%d ", *p);
        p++;    
    }
    // 1 2 3 4 5 0 0 0 0 0 
    return 0; 
}

指针习题
  1. 下列代码的运行结果是()
	int a[] = { 1,2,3,4 }; 
	int* b = a;
	*b += 2; 
	*(b + 2) = 2;
	b++; 
	printf(" %d, %d\n", *b, *(b + 2));

A 1,3
B 1,2
C 2,4
D 3,2

标准答案:

C

答案解析:

	b++

使得指向a[0]的指针 b 指向a[1] 。(没有此语句答案是D)

  1. 以下程序的输出结果是?
#include <stdio.h> 

int main() {
	char a[10] = { '1','2','3','4','5','6','7','8','9',0 }, * p; 
	int i;
	i = 8;
	p = a + i;
	printf("%s\n", p - 3);

	return 0;
}

A 6
B 6789
C ‘6’
D 789

正确答案:

B

答案解析:

p - 3 找到字符‘6’的位置,然后打印直到遇到\0

  1. 指针变量p进行自加运算(即 执行p++;)后,地址偏移值为1,则其数据类型为 char。说法是否正确?
  • 正确
  • 错误

正确答案: 错误

答案解析:

C:unsigned char C++:空类、bool
而且,指针p的数据类型应该是char*,应该错在这里

  1. int a = (int)(((int *)0) +4 ); a 等于多少?为什么?

a = 16,0 被强转为 int *,所以 + 4 表示偏移了4个int大小的地址(一个int为4个字节),所以 a = 16;

同理

int a = (int)(((char *)0) +4 )

a = 4

int a = (int)(((double *)0) +4 )

a = 32

int a = (int)(((void *)0) +4 )

转化为 void * 后 不能++

在这里插入图片描述

改正后的代码:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
	char src1[] = "hello, world";
	char* src = src1;
	char* dest = NULL;
	int len = strlen(src);
	dest = (char*)malloc(sizeof(src1));
	if (dest != NULL) {
		char* d = dest;
		char* s = src + len - 1;
		while (len-- != 0) {
			*d++ = *s--;
		}
		*d = '\0';
		printf("%s", dest);

		free(dest);
		dest = NULL;
	}
	return 0;
}

头文件中尖括号:编译时,将在系统包含目录中搜索头文件中的括号。如果找不到,则会在源代码所在的目录中对其进行搜索。

头文件中双引号:编译时,将在源代码所在的目录中搜索头文件中的双引号,如果未找到,将在系统包含目录中搜索该头文件中的双引号。

  1. 若二维数组a有m行n列,则下面能够正确引用元素 a[i][j] 的为() 。

A. * (a + j * n + i)
B. * (a + i * n + j)
C. * (*(a+i)+j)
D. * (*a+i)+j

正确答案: C、D

答案解析:

#include <iostream>
using namespace std;

// 此时 m = n = 3, i = j = 2, a[2][2] == 9
int main() {
    int a[3][3] = { 1,2,3,4,5,6,7,8,9 };
    cout << *(a + 2 * 3 + 2) << endl;    // 004FFAE0
    cout << *(*a + 2) + 2 << endl;       // 5
    cout << *(*(a + 2) + 2) << endl;     // 9
    cout << *(a[2] + 2) << endl;         // 9
    return 0;
}

如有不同见解,欢迎留言讨论!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值