C语言指针的详解和应用

一、指针简介

指针(Pointer)是C语言的一个重要知识点,其使用灵活、功能强大,是C语言的灵魂。指针与底层硬件联系紧密,使用指针可以操作数据的地址,实现数据的间接访问。指针是C语言区别于其他高级语言的关键特性之一。

二、计算机存储机制

在计算机系统中,小端(Little Endian)和大端(Big Endian)是两种不同的字节序(Byte Order)方式,它们定义了多字节数据在内存中的存储顺序。了解这些概念对于操作系统、网络协议和低级编程(如嵌入式系统)非常重要,数组的存储不受大小端的影响,例如数组是char型数组,其数据就按照顺序依次存储,但如果数组是short型的,那么其存储顺序虽然不受影响,但是每个元素的存储方式还是要按照大小端的定义来存储。

小端(Little Endian)

小端模式是将数据的低字节存储在内存的低地址处,而高字节存储在内存的高地址处。换句话说,数据的最低有效字节在最前面(最低地址),最高有效字节在最后面(最高地址)。

理解指针的基础是理解计算机的存储机制。计算机通过内存地址来访问数据,变量在内存中占据特定的地址空间。例如:

int a = 0x12345678;
short b = 0x5A6B;
char c[] = {0x33, 0x34, 0x35};
小端存储模式 

大端(Big Endian)

大端模式是将数据的高字节存储在内存的低地址处,而低字节存储在内存的高地址处。换句话说,数据的最高有效字节在最前面(最低地址),最低有效字节在最后面(最高地址)。

大端存储模式 

 三、定义指针

指针即指针变量,用于存放其他数据单元(变量/数组/结构体/函数等)的首地址。若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针。

义一个指针变量:

数据类型

指向该数据类型的指针

(unsigned) char

1字节

(unsigned) char *

x字节

(unsigned) short

2字节

(unsigned) short *

x字节

(unsigned) int

4字节

(unsigned) int *

x字节

(unsigned) long

4字节

(unsigned) long *

x字节

float

4字节

float *

x字节

double

8字节

double *

x字节

注:16位系统:x=232位系统:x=464位系统:x=8

 四、指针的操作

 定义一个指针变量后,可以对指针进行多种操作。假设有以下定义:

int a;  // 定义一个int型的数据
int *p;  // 定义一个指向int型数据的指针

则对指针p的操作方式如下:

操作方式

举例

解释

取地址

p=&a;

将数据a的首地址赋值给p

取内容

*p;

取出指针指向的数据单元

p++;

使指针向下移动1个数据宽度

p=p+5;

使指针向下移动5个数据宽度

p--;

使指针向上移动1个数据宽度

p=p-5;

使指针向上移动5个数据宽度

 如下例子所示:

#include <stdio.h>

int main(void)
{
	int a = 0x66;
	int *p=&a;
	
	printf("P++操作前地址:0x%x\n",p);
	p++;
	printf("P++操作后地址:0x%x\n",p);
	return 0 ;
 } 

运行结果如下:

 P指针向下移动了一个数据宽度,由于是int型数据,所以为4个字节,由0x62fe14变为0x62fe18。

 五、数组与指针

数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针。数组的定义等效于申请内存、定义指针和初始化。例如:

char c[] = {0x33, 0x34, 0x35};

等效于:

int *c;
c = malloc(3*4);
*c = 0x33;
*(c+1) = 0x34;
*(c+2) = 0x35;

 利用下标引用数组数据也等效于指针取内容。

例如:    

c[0]; 等效于*c;
c[1]; 等效于*(c+1);
c[2];等效于*(c+2);

六、注意事项

在使用指针时需要注意以下几点:

  1. 指针的合法性:在对指针取内容之前,一定要确保指针指向合法的位置,否则将会导致程序出现不可预知的错误。
  2. 指针赋值:同级指针之间才能相互赋值,跨级赋值将会导致编译器报错或警告。例如,int类型指针不能赋值给char类型指针。
  3. 指针运算:在进行指针运算时,必须注意指针的类型和步长。例如,int类型指针加1实际是增加4个字节(在32位系统中)。         

七、指针的应用

指针在C语言中的应用广泛,主要包括以下几个方面:

1. 传递参数
a. 使用指针传递大容量的参数

使用指针传递大容量的参数,可以避免参数传递过程中的数据复制,提高运行效率,减少内存占用。例如,我们有一个大数组需要传递给函数进行处理。

#include <stdio.h>

void processArray(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2; // 将数组中的每个元素乘以2
    }
}

int main() {
    int largeArray[1000];
    for (int i = 0; i < 1000; i++) {
        largeArray[i] = i;
    }

    processArray(largeArray, 1000); // 传递数组指针

    printf("largeArray[0] = %d\n", largeArray[0]); // 输出:largeArray[0] = 0
    printf("largeArray[1] = %d\n", largeArray[1]); // 输出:largeArray[1] = 2

    return 0;
}
b. 使用指针传递输出参数

使用指针传递输出参数,可以实现函数返回多个值。例如,我们可以通过指针返回两个整数的和与差。

#include <stdio.h>

void sumAndDifference(int a, int b, int *sum, int *difference) {
    *sum = a + b;
    *difference = a - b;
}

int main() {
    int a = 10, b = 5;
    int sum, difference;

    sumAndDifference(a, b, &sum, &difference); // 传递输出参数的指针

    printf("Sum: %d\n", sum);             // 输出:Sum: 15
    printf("Difference: %d\n", difference); // 输出:Difference: 5

    return 0;
}
2. 传递返回值

将模块内的公有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作。例如,使用指针返回一个动态分配的数组,并在主函数中操作它。

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

int* createArray(int size) {
    int *arr = (int *)malloc(size * sizeof(int));
    for (int i = 0; i < size; i++) {
        arr[i] = i * 10; // 初始化数组
    }
    return arr; // 返回指针
}

int main() {
    int size = 10;
    int *array = createArray(size); // 获得数组指针

    for (int i = 0; i < size; i++) {
        printf("array[%d] = %d\n", i, array[i]);
    }

    free(array); // 释放内存
    return 0;
}
3. 直接访问物理地址下的数据

在嵌入式系统中,经常需要直接访问硬件设备的特定内存地址。例如,访问某个设备的ID号。

#include <stdio.h>

// 假设设备ID存储在内存地址0x4000处
#define DEVICE_ID_ADDRESS 0x4000

int main() {
    unsigned int *deviceID = (unsigned int *)DEVICE_ID_ADDRESS;

    printf("Device ID: 0x%x\n", *deviceID); // 直接访问设备ID

    return 0;
}
 4. 数据格式转换
示例:将结构体转换为字节数组

我们将定义一个包含整数和浮点数的简单结构体,并展示如何将其序列化为字节数组,以及如何从字节数组中反序列化为结构体。

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

typedef struct {
    int id;
    float value;
} SimpleData;

// 序列化函数:将结构体复制到字节数组中
void serializeSimpleData(SimpleData *data, unsigned char *buffer) {
    memcpy(buffer, data, sizeof(SimpleData));
}

// 反序列化函数:从字节数组中恢复结构体
void deserializeSimpleData(unsigned char *buffer, SimpleData *data) {
    memcpy(data, buffer, sizeof(SimpleData));
}

int main() {
    SimpleData data = {42, 3.14};
    unsigned char buffer[sizeof(SimpleData)];

    // 序列化数据
    serializeSimpleData(&data, buffer);

    // 清空原数据结构
    memset(&data, 0, sizeof(SimpleData));

    // 反序列化数据
    deserializeSimpleData(buffer, &data);

    // 输出恢复后的数据
    printf("ID: %d\n", data.id);       // 输出:ID: 42
    printf("Value: %.2f\n", data.value); // 输出:Value: 3.14

    return 0;
}

八、指针函数

指针函数是指返回指针的函数。它的返回类型是一个指针,意味着函数返回一个地址,而不是一个具体的值。

示例:指针函数

假设我们有一个函数,它返回一个指向整数的指针:

#include <stdio.h>

// 定义指针函数,返回一个指向整数的指针
int* getPointerToInteger(int *num) {
    return num;
}

int main() {
    int a = 10;
    int *p;

    // 调用指针函数并获取返回的指针
    p = getPointerToInteger(&a);

    printf("Value of a: %d\n", *p); // 输出:Value of a: 10

    return 0;
}

函数指针

函数指针是指向函数的指针。它可以用来存储函数的地址,并通过它调用函数。这在实现回调函数和函数表(如操作系统中的系统调用表)时非常有用。

示例:函数指针

假设我们有两个函数,它们实现简单的数学运算,我们可以定义一个函数指针来调用它们。

#include <stdio.h>

// 定义两个简单的函数
int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

// 定义一个函数指针类型
typedef int (*operationFunc)(int, int);

int main() {
    // 声明函数指针
    operationFunc op;

    // 将函数指针指向add函数
    op = add;
    printf("Addition: %d\n", op(3, 4)); // 输出:Addition: 7

    // 将函数指针指向multiply函数
    op = multiply;
    printf("Multiplication: %d\n", op(3, 4)); // 输出:Multiplication: 12

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值