指针是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;
}
用途:
通常当作函数的输入参数,因为一级指针作为函数参数,在调用的时候,实参和形参是不同的内存空间,只是,这个内存空间存放的指针指向的是同一块地址 ,所以形参在函数执行中可以访问实参指向的内存空间,但是形参的指向的改变并不能影响实参。 总结一句话“一级指针做函数参数, 在函数内做形参做重新指向并不会影响实参的指向”
函数传参知识点:
- 当传入参数时,函数形参会立即申请形参的内存空间,函数执行完毕后,形参的内存空间立即释放掉。
- 函数参数传参本质:实参传给形参,都是拷贝
一级指针应用需要注意的:
- 不要在函数中,改变形参的指向来达到改变实参指向的效果,因为形参和实参只是两个指向同一空间的不同的指针。
- 因为形参和实参指向的是同一空间,所以可以在形参中改变其指向空间的值,如此实参指向的空间地址也发生改变。
代码实例:
#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;
}
指针习题
- 下列代码的运行结果是()
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)
- 以下程序的输出结果是?
#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
- 指针变量p进行自加运算(即 执行p++;)后,地址偏移值为1,则其数据类型为 char。说法是否正确?
- 正确
- 错误
正确答案: 错误
答案解析:
C:unsigned char C++:空类、bool
而且,指针p的数据类型应该是char*,应该错在这里
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;
}
头文件中尖括号:编译时,将在系统包含目录中搜索头文件中的括号。如果找不到,则会在源代码所在的目录中对其进行搜索。
头文件中双引号:编译时,将在源代码所在的目录中搜索头文件中的双引号,如果未找到,将在系统包含目录中搜索该头文件中的双引号。
- 若二维数组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;
}
如有不同见解,欢迎留言讨论!