由浅至深->C语言中指针及数组的经典问题分析(一)

引言:该系列第四篇文章。指针、数组是C语言中的重要内容,也是C语言学习者的痛点和难点,本文将尽力去说明其中的陷阱和槽点。

文章向导
* 指针的本质与总结
* 数组的本质与总结

一、指针的本质与总结

1.何为指针?

指针是一个其数值为地址的变量,就如 char 类型的变量用字符作为其数值,而 int 型变量的数值是整数。既然指针也是变量,那么其自身也应该被分配有地址,这与指针所指向的地址不同。

2.与指针相关的运算符
· 在指针声明时,*号表示所声明的变量为指针
· 在指针使用时,*号表示取指针所指向的内存空间中的值
· &运算符后跟一个变量名时,给出该变量的地址

int i = 0;
int j = 0;

int* p = &i; //指针声明

j = *p; //指针使用

这里写图片描述
这里可以做一个形象的比喻:*号相当于一把钥匙,通过这把钥匙可以打开内存并读取其中的值。而从C语言的描述上来说,p等价于&i,*p等价于i 。

3.指针声明时的小问题

你是否对以下这几种指针声明感到困惑,它们是一样的吗?

int* pi, int * pi, int *pi, int*pi;

事实上和指针名之间的空格是可选的,传统上C语言中使用int *pi;这强调*pi是一个int类型的值。C++中更倾向于使用int* pi;这强调int*是一种类型——指向int 的指针。但实际上在哪里添加空格对于编译器来说没有任何区别,即你可以写成int*pi;

4.指针占用的内存大小

#include <stdio.h>
int main()
{
    int i = 0;
    int* pI;
    char* pC;
    float* pF;

    pI = &i;
    *pI = 10;
    printf("%p, %p, %d\n", pI, &i, i);
    printf("%d, %d, %p\n", sizeof(int*), sizeof(pI), &pI);
    printf("%d, %d, %p\n", sizeof(char*), sizeof(pC), &pC);
    printf("%d, %d, %p\n", sizeof(float*), sizeof(pF), &pF);

    return 0;
}

上述程序的运行结果如下(32位VC++6.0编译平台):
这里写图片描述
此处指针所占用的内存大小,取决于系统位数。32位系统,结果为4;64位系统,结果为8.(牢记指针值是地址,而地址值与系统位数有关)

5.可以把指针看作是整数类型吗?

在大多数计算机系统内部,地址由一个无符号整数表示,但这并不意味着可把指针看作是整数类型。比如下面这个例子:

#include <stdio.h>
int main()
{   
    float f = 8.25;

    unsigned int * p = (unsigned int*)&f;
    //地址在大多数系统内部由无符号整数表示,此处的*表示该变量为一指针。
    //右边若改为(unsigned int)&f则会出现警告:初始化时将整数赋给指针,未作类型转换。这是因为C语言中并不会自动地做强制类型转换,两边运算类型要匹配才行。

    //08代表最小字段宽度为8,X代表使用十六进制数字0F的无符号十六进制整数。
    printf(“0x%08X\n”,*p); //0x41040000

    return 0;
}

由上面这个例子可知:指针的确是一种新的数据类型,而并非一种整数类型。

6.传值调用和传址调用

  • 函数调用时实参值将复制到形参(传值调用)
  • 当一个函数体内部需要改变实参的值,则需要使用指针参数(传址调用)
/*传址调用实例*/
#include <stdio.h>
int swap(int* a, int* b)
{
    int c = *a;
    *a = *b;
    *b = c;
}
int main()
{
    int aa = 1;
    int bb = 2;
    printf("aa = %d, bb = %d\n", aa, bb); // 1 2
    swap(&aa, &bb); //传址调用
    printf("aa = %d, bb = %d\n", aa, bb); // 2 1
    return 0;
}

7.常量与指针

  • 几种指针的声明形式
const int* p; //p 可变,p 指向的内容不可变
int const* p;//p 可变,p 指向的内容不可变
int* const p;//p 不可变,p 指向的内容可变
const int* const p;//p 和 p 指向的内容都不可变
//这里的不可变仅仅指的是不能通过p本身来改变,不考虑其他手段。
  • 一种名为“左数右指”的记忆方法
    -当 const 出现在*号左边时指针指向的数据为常量;
    -当 const 出现在*号右边时指针本身为常量。
  • 实例验证
#include <stdio.h>
int main()
{
    int i = 0;
    const int* p1 = &i;
    int const* p2 = &i;
    int* const p3 = &i;
    const int* const p4 = &i;

    *p1 = 1; // compile error
     p1 = NULL; // ok

    *p2 = 2; // compile error
     p2 = NULL; // ok

    *p3 = 3; // ok
     p3 = NULL; // compile error

    *p4 = 4; // compile error
     p4 = NULL; // compile error

    return 0;
}

二、数组的本质与总结

1.何为数组(array)?

数组是由一组类型相同的元素所组成的有序集合,比如声明一个int[5]的整型数组,书写简单但其内涵却可深入挖掘。具体如下图所示:
这里写图片描述

2.数组的大小与初始化

1) 初始化时应注意的细节

int a[5] = {1,2,3,4,5}; //显示指定数组元素的个数
int b[] = {1,2}; //隐式指定
int c[5] = {0}; //第一个元素为 0,其余未指定的编译器默认初始化为 0,故全为0

2) 如何自动计算数组元素的个数
假定有一int型数组a,可通过下面的算式快速得出数组元素个数。

int a[5] = {0};

printf("count for a : %d\n", sizeof(a)/sizeof(int));

3.数组首元素地址与数组地址

1)需要明确的几点
- 数组名代表数组首元素的地址
- 数组的地址需要用取地址符&才能得到
- 数组首元素的地址与数组的地址,两者在值上相同
- 数组首元素的地址与数组的地址是两个不同的概念(即虽然两者在值上是相
等的,但所指示的数据长度不同:前者为第一个数据的长度,后者为整个数
组的数据长度。)

2)实例验证

#include <stdio.h>
int main()
{
    int a[5] = { 0 };
    printf("a = %p\n", a); 
    printf("&a = %p\n", &a); 
    printf("&a[0] = %p\n", &a[0]); 
    return 0;
}

4.数组名的盲点知识

  • 数组名可以看作一个常量指针(只是看作,但本质数组和指针是不同的)
  • 数组名“指向”的是内存中数组首元素的起始位置
  • 数组名不包含数组的长度信息
  • 在表达式中数组名只能作为右值使用
  • 当数组名作为sizeof操作符的参数或&运算符的参数时,不能看作常量指针
/*32位机上测试结果*/
#include <stdio.h>
int main()
{
    int a[5] = {0};
    int b[2];
    int* p = NULL;

    p = a;
    printf("a = %p\n", a);
    printf("p = %p\n", p);
    printf("&p = %p\n", &p);

    printf("sizeof(a) = %d\n", sizeof(a)); //20
    printf("sizeof(p) = %d\n", sizeof(p)); //4
    printf("\n");

    p = b;
    printf("b = %p\n", b);
    printf("p = %p\n", p);
    printf("&p = %p\n", &p);

    printf("sizeof(b) = %d\n", sizeof(b)); //8
    printf("sizeof(p) = %d\n", sizeof(p));//4

    b = a; //error

    return 0;
}

参阅资料
C Primer Plus(第五版)
C++ Primer Plus(第六版)
狄泰软件学院-C进阶剖析教程
高质量嵌入式Linux C编程

猜你喜欢

转载自blog.csdn.net/a574780196/article/details/80617180