【C语言】 指针基础与定义

指针的基本定义和使用

指针是C语言中一个核心且强大的概念,它允许程序直接访问和操作内存地址。理解指针,首先要理解内存和地址的基本概念。

内存与地址

计算机内存是由一系列连续的存储单元组成,每个单元都有一个唯一的地址。变量在内存中占用一定的空间,其地址就是该空间起始位置的编号。

指针是什么?

指针是一种变量,其存储的值是另一个变量的内存地址。通过指针,我们可以间接访问和修改该地址对应的数据。

指针的基本语法

c 复制代码
int a = 100;        // 定义一个整型变量a
int *p = &a;        // 定义一个整型指针p,并将a的地址赋值给p
  • &a 表示取变量 a 的地址。
  • int *p 表示 p 是一个指向 int 类型的指针。
  • *p 表示访问 p 指向的内存内容(解引用)。

指针的使用示例

c 复制代码
#include <stdio.h>

int main() {
    int a = 100;
    int *p = &a;

    printf("a = %d\n", a);      // 输出: a = 100
    printf("*p = %d\n", *p);    // 输出: *p = 100

    *p = 200;                   // 通过指针修改a的值
    printf("a = %d\n", a);      // 输出: a = 200

    return 0;
}

指针与变量的关系

  • 变量名是内存空间的标识符。
  • 指针存储的是该内存空间的地址。
  • 通过指针可以间接访问和修改变量的值,实现"远程操作"。

不同类型指针之间的使用

指针的类型必须匹配

指针的类型决定了它指向的数据类型。不同类型的指针不能混用,否则可能导致未定义行为。

c 复制代码
char ch = 'A';
int num = 100;

int *p = &ch;  // 错误:类型不匹配,char* 不能赋给 int*

类型转换与指针

可以使用强制类型转换,但必须谨慎,因为可能引发内存访问错误。

c 复制代码
char ch = 'A';
int *p = (int*)&ch;  // 强制转换,语法正确但危险
*p = 1000;           // 可能导致内存越界

示例:类型不匹配的危险

c 复制代码
#include <stdio.h>

int main() {
    char ch = 'A';
    int *p = (int*)&ch;  // 强制转换

    printf("ch = %c, %d\n", ch, ch);  // 正常输出
    *p = 1000;                        // 写入4字节,但ch只有1字节
    // 可能导致程序崩溃或数据错误
    return 0;
}

指针所占内存空间的大小

指针的大小与系统架构相关

  • 在32位系统中,指针通常为4字节。
  • 在64位系统中,指针通常为8字节。

所有指针的大小相同

无论指针指向什么类型(intchardouble 等),其大小都相同,因为它存储的只是地址。

c 复制代码
#include <stdio.h>

int main() {
    printf("sizeof(int*) = %zu\n", sizeof(int*));
    printf("sizeof(char*) = %zu\n", sizeof(char*));
    printf("sizeof(double*) = %zu\n", sizeof(double*));
    printf("sizeof(void*) = %zu\n", sizeof(void*));
    return 0;
}

常见的地址类型以及获取地址的方法

取地址运算符 &

& 用于获取变量的地址,其类型为"数据类型*"。

c 复制代码
int a;           // &a 的类型为 int*
double b;        // &b 的类型为 double*
int c[5];        // &c 的类型为 int (*)[5]
int *d;          // &d 的类型为 int**
char e[3][4];    // &e 的类型为 char (*)[3][4]

数组的地址

数组名在大多数情况下表示数组首元素的地址,类型为"元素类型*"。

c 复制代码
int a[3];          // a 的类型为 int*(指向 a[0])
char b[3][4];      // b 的类型为 char (*)[4](指向 b[0])
int *c[5];         // c 的类型为 int**(指向 c[0])

例外情况:

  • &数组名:表示整个数组的地址,类型为"数组类型*"。
  • sizeof(数组名):返回整个数组的大小。

数组元素类型的计算

在定义数组时,去掉数组名和第一组[],剩下的就是元素类型。

c 复制代码
int a[3];          // 元素类型为 int
char b[3][4];      // 元素类型为 char [4]
int *c[5];         // 元素类型为 int*
int *d[4][5];      // 元素类型为 int (*)[5]

指针的定义与初始化

定义方法

指针的定义遵循"数据类型 *变量名"的形式。

c 复制代码
int *p;           // 定义一个指向int的指针
char *pc;         // 定义一个指向char的指针
double *pd;       // 定义一个指向double的指针

初始化

指针在使用前最好进行初始化,否则可能成为"野指针"。

c 复制代码
int a = 10;
int *p = &a;      // 正确初始化
int *q = NULL;    // 初始化为空指针

易错形式

c 复制代码
int *c, d;        // 只有c是指针,d是普通int
int *e, *f;       // e和f都是指针

指针的要素

指针的三个要素

  1. 指针本身的类型 :如 int*char*
  2. 指针的数值:存储的地址值。
  3. 指针的指向对象类型:决定了解引用时访问的空间大小和类型。
c 复制代码
int a = 100;
int *p = &a;
  • p 的类型:int*
  • p 的数值:&a
  • p 的指向对象类型:int

指向对象类型的意义

指向对象类型决定了:

  • *p 访问的内存大小(如 int 为4字节)。
  • *p 的数据类型。

野指针

什么是野指针?

野指针是指未被初始化或指向无效内存的指针。使用野指针可能导致程序崩溃。

c 复制代码
int *p;          // 未初始化
*p = 10;         // 错误:访问未知内存

避免野指针的方法

  • 初始化指针为 NULL
  • 使用前检查指针是否有效。
c 复制代码
int *p = NULL;
if (p != NULL) {
    *p = 10;
}

空指针 NULL

NULL 是一个表示"空地址"的宏,通常定义为0。

c 复制代码
int *p = NULL;
if (p == NULL) {
    printf("指针为空\n");
}

指针的相关运算

算术运算

指针只能进行加法和减法运算,单位是指向对象类型的大小。

c 复制代码
int a[5] = {1, 2, 3, 4, 5};
int *p = &a[0];

p = p + 1;   // 指向 a[1],地址增加 sizeof(int) 字节
p = p - 1;   // 指回 a[0]

示例:

c 复制代码
int *p = NULL;
p = p + 1;   // 地址增加 4 字节(假设int为4字节)

double *q = NULL;
q = q + 2;   // 地址增加 16 字节(假设double为8字节)

指针相减

两个同类型指针相减,结果为它们之间的元素个数。

c 复制代码
int a[5] = {1, 2, 3, 4, 5};
int *p1 = &a[0];
int *p2 = &a[3];

printf("%ld\n", p2 - p1);   // 输出: 3

关系运算

指针可以进行比较(==!=<> 等),通常用于数组遍历。

c 复制代码
int a[5];
int *p = a;
while (p < a + 5) {
    printf("%d ", *p);
    p++;
}

赋值运算

指针之间可以直接赋值,但类型必须匹配。

c 复制代码
int a = 10;
int *p = &a;
int *q = p;   // q 也指向 a
相关推荐
Ekehlaft11 小时前
这款国产 AI,让 Python 小白也能玩转编程
开发语言·人工智能·python·ai·aipy
rit843249911 小时前
MATLAB中Teager能量算子提取与解调信号的实现
开发语言·matlab
开源技术11 小时前
Python GeoPandas基础知识:地图、投影和空间连接
开发语言·ide·python
Cult Of11 小时前
Alicea Wind的个人网站开发日志(2)
开发语言·python·vue
我找到地球的支点啦11 小时前
通信扩展——扩频技术(超级详细,附带Matlab代码)
开发语言·matlab
啊阿狸不会拉杆11 小时前
《机器学习导论》第 5 章-多元方法
人工智能·python·算法·机器学习·numpy·matplotlib·多元方法
微小冷12 小时前
Rust异步编程详解
开发语言·rust·async·await·异步编程·tokio
A9better12 小时前
C++——不一样的I/O工具与名称空间
开发语言·c++·学习
清水白石00812 小时前
《为什么说 deque 是 Python 滑动窗口的“隐藏神器”?深入解析双端队列的高效之道》
开发语言·python