C语言指针:探索内存与数据的连接,基础入门(超详细!)

        C语言中的指针是一种强大而重要的概念。它提供了访问内存中的位置和数据的能力,使程序能够更高效地操作和处理数据。本文将逐步介绍C语言中指针的基础知识,帮助读者深入理解指针的原理和用法。

一、指针的基本概念  

指针的定义和作用,以及为什么需要指针:

先讲一个例子:当我们入住酒店的时候都会给我们一个门牌号,来让我们快速找到对应房间。计算机也是,将各种数据存储在地址里面方便我们进行访问,所以我们可以理解为:

内存单元的编号  ==  地址  ==指针,指针就是地址;方便CPU在快速通过内存编号找到对应的内存空间。

二、 指针变量和地址

      二、1  取地址操作符(&)

取地址操作符(&)在C语言中是一种重要的运算符,用于获取变量的地址。以下是对取址操作符详细解释的文字描述:

取地址操作符(&)是一种用于获取变量地址的运算符。在C语言中,每个变量都在计算机的内存中分配了一个唯一的地址,这个地址可以用来访问和操作变量。

使用取址操作符的语法形式为:&variable

其中,variable 是要获取地址的变量名。

通过取址操作符,我们可以获取该变量在内存中的地址,并将其赋给指针变量。这样,指针变量将指向该变量所在的内存地址,从而可以通过指针来操作该变量。

例如,假设我们有一个整数变量 num,如果我们需要获取 num 的地址,我们可以使用取址操作符 &,如下所示:

int num = 10;
int *ptr = # // 获取 num 的地址,并将其赋给指针变量 ptr

在上面的示例中,首先定义了一个整数变量 num,并将其初始化为 10。然后,通过 &num 来获取 num 的地址,使用 int * 类型的指针变量 ptr 来存储该地址。现在,指针变量 ptr 将指向变量 num 在内存中的地址。

取地址操作符可以应用于任何基本数据类型的变量,包括整数、浮点数、字符等。同时,还可以用于结构体、数组等复合数据类型。通过使用取址操作符,我们可以方便地获取任何变量在内存中的地址,并使用指针来对其进行访问和操作。

需要注意的是,取址操作符只能用于普通变量,不能应用于常量或表达式。这意味着我们无法获取字面量或表达式的地址。

        二、2  指针变量和解引⽤操作符(*)

指针变量

        指针变量就是一种变量,存放的类型为指针。在我们创建指针变量时,要确定其数据类型:  <数据类型> *   <指针变量名>

//创建一个整型变量
int* p1;
//创建一个字符变量
char* p2;

  当然指针变量也要进行初始化:常见的初始化方法有以下几种:

•  将指针变量指向另一个已经存在的变量的地址:

int num = 10;
int *ptr = &num;

 • 将指针变量设为 NULL,表示它不指向任何有效的地址:

int *ptr = NULL;
  解引⽤操作符

 

解引用操作符(*)是一种用于访问指针所指向的内存中的数据的运算符。通过解引用操作符,我们可以读取和修改指针所指向的值。以下是对解引用操作符的详细解释:

  1. 访问指针所指向的值:
    使用解引用操作符可以获取指针所指向的存储在内存中的值。例如:

    int num = 10;
    int *ptr = &num;
    printf("%d\n", *ptr); // 输出指针变量所指向的值

    在上述例子中,ptr 是一个指向整数的指针变量,通过解引用操作符 *,我们可以获取指针所指向的整数值 num 并进行打印。

  2. 修改指针所指向的值:
    解引用操作符还允许我们修改指针所指向的值。例如:

    int num = 10;
    int *ptr = &num;
    *ptr = 20; // 修改指针变量所指向的值
    printf("%d\n", num); // 输出修改后的值
    

    在上述例子中,通过解引用操作符 * 和赋值操作,我们将指针变量 ptr 所指向的值修改为 20。因为 ptr 和 num 指向同一个地址,所以对指针解引用的修改也会反映在原始的变量 num 上。

       指针变量的大小

指针变量的大小在C语言中是由编译器所决定的,它取决于编译器的实现以及所运行的操作系统的位数。以下是对指针变量大小的详细解释:

  1. 32位系统:
    在32位系统中,指针变量的大小通常为4个字节(32位)。这是因为在32位系统中,内存地址的大小为32位,也就是4个字节。指针变量存储的是内存地址,因此它也需要占用4个字节的空间来存储地址值。

  2. 64位系统:
    在64位系统中,指针变量的大小通常为8个字节(64位)。这是因为在64位系统中,内存地址的大小扩展为64位,即8个字节。指针变量需要能够容纳更大范围的地址值,因此需要占用8个字节的空间来存储地址。

需要注意的是,指针变量的大小并不会随着所指向的数据类型的大小而变化。无论指针所指向的是一个字节还是一个更大的数据结构,指针变量的大小仍然是固定的。

另外,还需要了解的是,不同的编译器和操作系统可能有所不同,它们对于指针变量的大小可能有不同的实现。因此,当涉及到与指针大小相关的具体数值时,最好使用 sizeof 运算符来获取指针变量的实际大小。

总结起来,指针变量的大小取决于系统的位数,32位系统中通常为4个字节,64位系统中通常为8个字节。这些大小值是根据地址空间的大小来确定的,它们在编译器和操作系统的规范中被定义。

指针类型的意义

既然指针是存放的地址的,都是一串数字那么指针的类型还有什么意义呢?其实指针的类型十分重要!!!

比如

//整型指针
#include<stdio.h>
int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	int* p1 = arr;
	char* p2 = (char*)arr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ",p1[i]);
	}
	printf("\n");

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p2[i]);
	}
}

  运行结果如下

为什么用char类型的指针打出来这么多  0  呢?

我们知道int型的指针是4个字节,而char型的指针是1个字节;所以在访问的时候int型的指针一次性跳过4个字节,而char型的指针跳过1个字节。

总结:指针的类型决定了指针向前或者向后走一步有多大的距离。

 void *  指针

 在C语言中,void* 是一种特殊类型的指针,它可以表示任意类型的指针。它的功能类似于一个容器,可以存储指向任何类型数据的地址,而不需要显式指定具体的类型。

void* 指针在C语言中的使用具有以下几个重要的用途:

1. 泛型指针

void* 指针可以用作通用或泛型指针,用于处理未知数据类型的操作。在某些情况下(如通用算法库),我们可能需要编写能够处理任意类型数据的代码。通过使用 void* 指针,我们可以在不了解实际类型的情况下进行数据操作,稍后在需要时,再将 void* 指针转换为恰当的类型。

2. 动态内存分配

在动态内存分配中,我们通常使用 void* 指针返回内存分配函数(如 malloccallocrealloc)返回的内存地址。由于这些函数无法确定返回的数据类型,因此它们返回的是 void* 指针。我们可以根据需要将其转换为适当的指针类型,然后使用该指针处理分配得到的内存。

3. 函数回调

使用 void* 指针传递函数回调参数可以实现更灵活的函数接口。通过将回调函数的指针作为 void* 参数传递给其他函数,我们可以在运行时决定要传递的函数,并在适当的时候将 void* 指针转换为实际的函数指针类型,以便执行相应的操作。

使用 void* 指针的注意事项

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

  1. 内存安全性:由于 void* 指针可以表示任何类型的指针,因此在使用时需要格外小心,以确保不会发生类型不匹配或错误的内存访问。在转换 void* 指针为其他类型指针时,要确保转换的目标类型与原始类型兼容。

  2. 隐式转换:在使用 void* 指针时,需要手动进行指针类型转换,以确保正确解引用和操作数据。对于指针的隐式转换是不安全的,并且可能导致未定义的行为。

  3. 丢失类型信息:由于 void* 指针丢失了原始数据类型的信息,无法直接了解指针所指向的数据的具体类型。因此,使用 void* 指针时需要确保在恢复原始类型之前,有一种机制来准确地知道指针所指向的数据类型。

在使用 void* 指针时,请牢记上述注意事项并小心处理,以免引起安全问题和运行时错误。

void* 指针是C语言中强大而灵活的概念,它允许存储和操作任意类型的指针。通过适当地应用,void* 指针可以帮助我们处理不同类型的数据,实现泛型编程和动态内存分配,为函数回调提供灵活性。但要记住,在使用 void* 指针时需要格外小心,确保类型匹配和正确性,以避免潜在的错误。

同时,由于void 是无类型的指针所以不能用于指针的+-整数和解引用的运算。

在C语言中,const 关键字可以用来修饰指针变量,将其声明为指向常量的指针。这意味着通过这个指针无法修改所指向的数据。可以将 const 关键字放置在指针变量前或星号后,两种方式的效果是相同的。

int num = 10;
int a = 30;
const int* ptr1 = &num;    // 指向常量的指针,指向的数据为常量
int const* ptr2 = &num;    // 同样是指向常量的指针
ptr2 = &a;		/*const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。*/

int* const ptr3 = &num;    /* const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。const如果放在* 的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。*/

*ptr3 = 20;
const int* const ptr4 = &num;    // 常量指针,指向的数据为常量,不可修改

const修饰指针

以上示例中,ptr1 和 ptr2 都是指向常量的指针,不能通过它们来修改 num 的值。ptr3 是一个常量指针,不能让它指向其他地址。ptr4 同时具有常量指针和指向常量的性质。

使用 const 修饰指针的好处

使用 const 修饰指针变量可以提供一些好处:

1. 数据保护

通过将指针声明为指向常量的指针,可以防止无意修改数据。这对于保护关键数据和确保数据的一致性非常重要。

2. 提高代码的可读性和可维护性

使用 const 修饰指针可以使代码更加清晰明了,读者可以快速理解这些指针的使用方式。此外,const 修饰符还可以作为文档,指示某些指针不应该被修改,从而提高代码的可维护性。

3. 编译器验证

编译器可以利用 const 修饰符来进行一些优化,例如避免不必要的拷贝和存储空间的优化,以提高代码效率。

注意事项

在使用 const 修饰指针时,需要注意以下几点:

  • const 修饰的指针不能通过该指针来修改所指向的数据,但可以通过其他指针修改。
  • const 修饰的指针可以重新赋值为其他指针,但赋值后不能通过这个指针来修改所指向的数据。
  • 在函数参数中使用 const 修饰的指针可以防止在函数内部对指针指向的数据进行修改。
  • const 修饰的指针可以指向非常量或常量对象,但指向常量的指针不能直接转换为指向非常量的指针。
总结

const 修饰指针是C语言中非常有用的一个概念,它指定了一个指针变量为只读,防止对所指向的数据进行修改。通过使用 const 修饰指针,我们可以提供数据保护、提高代码可读性和可维护性,并允许编译器进行一些优化。

在实际编程中,合理使用 const 修饰指针可以使代码更加健壮和可靠。同时,也应该注意使用 const 修饰指针的注意事项,以避免潜在的错误。继续学习和实践,你会逐渐掌握 const 修饰指针的使用技巧,并写出更高质量的代码。

野指针

学习C语言时,可能会遇到一个叫做"野指针"的概念,可以理解为指针变量没有正确初始化或指向了无效的内存地址。野指针是一个常见的编程错误,可能导致程序崩溃、内存泄漏等问题。本文将向新手介绍野指针的概念、原因以及如何避免野指针问题。

什么是野指针?

在C语言中,野指针是指指针变量存储的地址没有被正确初始化,或者指向了无效的内存地址。这意味着野指针没有明确指向有效的内存位置,可能会导致程序出现不可预测的行为。

以下是一个野指针的示例:

int* ptr; // 一个未初始化的指针变量
*ptr = 10; // 未初始化的指针,指向了无效的内存地址

在上述示例中,ptr 是一个未初始化的指针,它没有合法的内存地址。直接对其解引用会导致未定义的行为,很可能引发程序崩溃或产生错误结果。

野指针的原因

产生野指针的常见原因包括:

  1. 没有初始化指针变量:如果没有将指针变量初始化为有效的地址,它将成为一个野指针。

  2. 释放了指针指向的内存却没有将指针置为NULL:当我们释放了指针指向的内存后,如果没有将指针置为NULL,指针仍然保留之前释放的内存地址,导致野指针问题。

  3. 指针超出作用域:如果指针超出了其作用域,指针将失去对该内存位置的访问权限,变成了一个野指针。

  4. 返回局部变量的指针:如果返回了一个指向局部变量的指针,当局部变量超出作用域后,指针将变成野指针。

如何避免野指针问题?

避免野指针问题的关键是良好的编码实践:

  1. 初始化指针:在使用指针之前,始终确保将其初始化为一个有效的内存地址,或者将其设为NULL

  2. 及时置空:在释放指针所指向的内存后,立即将指针置为NULL,以避免之后对野指针的误用。

  3. 减小指针的作用域:尽量将指针的作用域限制在需要访问的范围内,确保在指针超出作用域后不再使用它。

  4. 注意函数返回值:避免返回指向局部变量的指针;如果确实需要返回指针,确保指针指向的内存在函数返回后仍然有效。

  5. 使用动态内存分配函数:当使用动态内存分配函数(如malloccallocrealloc)分配内存时,需要检查分配是否成功。如果分配未成功,以避免得到一个无效的指针。

总结

野指针是C语言中常见的错误之一,可能导致程序崩溃、内存泄漏等严重问题。避免野指针问题需要遵循良好的编程实践,包括初始化指针、及时置空、减小指针作用域、注意函数返回值和使用动态内存分配函数。

                                                                

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值