问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C语言指针加1的那些坑,你踩过几个?

创作时间:
作者:
@小白创作中心

C语言指针加1的那些坑,你踩过几个?

引用
CSDN
9
来源
1.
https://blog.csdn.net/DucklikeJAVA/article/details/90251788
2.
https://blog.csdn.net/suifengme/article/details/136208588
3.
https://blog.csdn.net/a812417530/article/details/116699396
4.
https://blog.csdn.net/qq_33266987/article/details/80842413
5.
https://blog.csdn.net/A642960662/article/details/123032607
6.
https://www.cnblogs.com/zppsky16/p/15665123.html
7.
https://www.cnblogs.com/CodeWorkerLiMing/p/12831719.html
8.
https://my.oschina.net/emacs_7871027/blog/15384068
9.
https://developer.aliyun.com/article/1336477

在C语言中,指针加1的操作远比想象的要复杂。它不仅与数据类型密切相关,还涉及到内存对齐、结构体布局等深层次问题。本文将通过具体示例,揭示指针加1中隐藏的陷阱,帮助你写出更健壮的代码。

01

指针加1的基本原理

首先需要明确的是,指针加1并不是简单地将地址值加1。它的实际效果取决于指针所指向的数据类型。具体来说,指针加1会使指针指向下一个逻辑上的数据元素,而这个"下一个"的位置是由数据类型大小决定的。

例如,对于一个指向char类型的指针,加1会使其向后移动1个字节;而对于一个指向int类型的指针(假设int占4字节),加1则会使其向后移动4个字节。

char a = 'a';
char *p = &a;
printf("%p %p\n", (void*)p, (void*)(p + 1)); // 输出: 0012FF33 0012FF34,偏移1字节

int i = 1;
int *q = &i;
printf("%p %p\n", (void*)q, (void*)(q + 1)); // 输出: 0012FF30 0012FF34,偏移4字节

02

结构体中的指针加1

结构体的内存布局为指针加1带来了更多不确定性。结构体的成员可能因为数据对齐的要求而存在填充字节,这会影响指针加1的结果。

例如,考虑以下结构体:

#pragma pack(1)
struct tree
{
  int height;
  int age;
  char tag;
};
#pragma pack()

在这个结构体中,heightage各占4字节,tag占1字节。如果没有特殊的编译指令,编译器可能会在tag后面添加3个填充字节,使得整个结构体占用12字节,以满足对齐要求。

但是,通过#pragma pack(1)指令,我们告诉编译器不要添加填充字节,因此这个结构体实际占用9字节。

现在,如果我们对指向这个结构体的指针进行加1操作,结果会怎样呢?

char buffer[512];
char *tmp_ptr = NULL;
struct tree *t_ptr = NULL;
char *t_ptr_new = NULL;

tmp_ptr = buffer;
t_ptr = (struct tree *) tmp_ptr;
t_ptr_new = (char *)(t_ptr + 1);

printf("t_ptr_new point to buffer[%ld]\n", t_ptr_new - tmp_ptr);

由于我们使用了#pragma pack(1),结构体的实际大小是9字节,因此t_ptr_new会指向buffer的第10个字节位置。

03

数组与指针的陷阱

数组和指针的关系是C语言中另一个容易出错的地方。特别是当涉及到指针数组和指向数组的指针时,很容易混淆。

char *strings[SIZE]; // 这是一个指针数组,包含SIZE个char*类型的元素
char (*str)[SIZE];   // 这是一个指向char数组的指针

int (*arr)[SIZE];    // 这是一个指向int数组的指针
int *ptr[SIZE];      // 这是一个指针数组,包含SIZE个int*类型的元素

在上面的代码中,stringsptr是数组,而strarr是指针。它们的大小也不同:

printf("size of str = %zd, size of strings = %zd\n", sizeof(str), sizeof(strings));
printf("size of arr = %zd, size of ptr = %zd\n", sizeof(arr), sizeof(ptr));

输出结果表明,指针的大小是8字节(在64位系统上),而数组的大小则是指针大小乘以数组长度。

04

常见错误与最佳实践

  1. 类型转换错误:在进行指针运算时,一定要注意类型转换。错误的类型转换会导致意想不到的结果。
t_ptr_new = (char *)(t_ptr + 1);

如果省略(char *),编译器会产生警告,因为类型不兼容。

  1. 数组越界:在使用指针遍历数组时,要特别小心不要越界。
for (int i = 0; i < SIZE; ++i) {
    printf("%d\n", *ptr[i]);
}
  1. 野指针:在释放动态分配的内存后,要将指针置为NULL,防止形成野指针。
free(dynamicArray);
dynamicArray = NULL;
  1. 字符串字面量:不要尝试修改字符串字面量,它们通常位于只读存储区。
char *strLiteral = "Hello, World!";
// strLiteral[0] = 'H'; // 这将导致未定义行为

通过理解指针加1的原理,以及在结构体、数组中的行为,我们可以避免许多常见的编程错误。记住,指针是C语言中最强大的特性之一,但同时也最容易出错。在使用指针时,始终保持谨慎,注意类型安全,合理使用类型转换,可以让你的代码更加健壮和高效。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号