C语言:初阶指针

简介: C语言:初阶指针

内存和地址

在计算机中,一切的运算数据都会暂存在内存中,当我们需要调用内存中某个部分的时候,就会用到指针。我们可以把计算机的内存比作一条街上的一排房屋,每个房屋都会容纳一定的数据,并可以用房号来标识;首先这个比喻是有一定的局限性的,不过对于初学者来理解,是足够的;这里就不必展开了;那这个房屋得多大呢?在计算机中内存中由数亿上计的位(bit)组成,所以,房屋储存以字节为单位;


假设把上面一个格子当作一个字节,那么每个格子就可存8个位;内存中每个位置都包含一些值;每个字节的位置用地址来标识;在这里,地址是十六进制的;为了存储更大的值,我们把两个或更多个字节合在一起作为一个更大的内存单位。我们的系统中,地址按字节编址,short类型占用2字节,double类型占用8字节;在这里,内存中的每个位置都是由独一无二的地址标识的;当我们要引用某个数据时,就要调用到某个内存,我们通过地址来找到该位置,而指针,就是来记住这个地址的;


指针

所以,指针是内存中一个最小单元的编号,也就是地址,口语中说的指针通常指的是指针变量。

如:

#include <stdio.h>
int main()
{
 int a = 10;
 int *p = &a;
  //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量。
 return 0;
}


对于变量a来说,会在内存中开辟一块空间,对于变量a,我们要取到它的地址,可以用到&操作符。

一般来说,在32位机器上,一个指针变量大小为4个字节,64位机器上,8个字节。

值与类型

先看下面代码:

#include <stdio.h>
int main()
{
 int a=112,b=1;
 float c=3.14;
 int* d=&a;
 int* e=&c;
 return 0;
}


变量表示:

我们知道a是整型存储类型的,对于a取地址,对他解引用是它本身,但是,c是浮点型类型的,对他取地址后解引用,却是一个整数;答案是该变量包含了一系列0或1的位,对于它是整型类型或者是浮点型类型,取决于它的算术指令;所以,不能简单的通过检查一个值的位来判断它的类型;


接着看以下代码:

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL


指针定义类型为 type+*;

对于不同类型的指针,是为了存储相应类型的地址;如char类型指针存储char变量的地址。那不同类型的指针又有什么不同呢?

#include <stdio.h>
int main()
{
 int n = 10;
 char *pc = (char*)&n;//括号是强制转换
 int *pi = &n;
 printf("%p\n", &n);
 printf("%p\n", pc);
 printf("%p\n", pc+1);
 printf("%p\n", pi);
 printf("%p\n", pi+1);
 return  0;
}


结果:

我们可以看到,对于char类型和int类型取地址,是位于n内存上的地址,

地址加1后,

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

最后:

//演示实例
#include <stdio.h>
int main()
{
 int n = 0x11223344;//表示地址位置
 char *pc = (char *)&n;
 int *pi = &n;
 *pc = 0;  
 *pi = 0; 
 return 0;
}


一开始的地址:


0x11223344


pc解引用后:


0x11223300


pi解引用后:


0x00000000


在这里,电脑采用了小端的存储方式。在电脑中,数据一定是从内存的低地址依次向高地址读取和写入,小端存储,会将低位存储到低地址处,高地址存储到高地址处,也即存储时是0x44332211.所以,pc解引用后,由于它是char类型的,大小只有一个字节,所以只对一个字节内存大小赋值为0,即为44;而pi为int类型,大小4个字节,所以当它解引用后,就会使整个地址为0。


总结:

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。


指针运算

指针±整数

#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
int main()
{
    for (vp = &values[0]; vp < &values[N_VALUES];)
{
     *vp++ = 0;
}
 }


在这里,values表示一个数组,vp是一个浮点型指针,在for循环中,vp赋值为value首元素地址,每当它+1后,地址就会不断先后移动4个字节,直至与values[5]地址相同,循环停止;

指针-指针

int my_strlen(char *s)
{
       char *p = s;
       while(*p != '\0' )
              p++;
       return p-s;
}


这是一个求字符串长度的函数,假设有一个字符串为“abcdef" ,指针位于初始位置a地址上,p指针表示初始位置地址,当循环之后,p走到了’\0’的位置,最后相减返回,即为字符串长度6。

当然,这里给出结论:指针-指针是返回的是之间有多少个元素,也即地址差/整型字节。一般可以运用到字符串和数组中。上面例子由于是char类型,看不出来。下面给出一个例子:

#include<stdio.h>
int main()
{
    int arr[10];
    printf("%d",&arr[10]-&arr[0]);
    return 0;
}


结果:10

当然指针-指针的前提条件是指向同一块空间。

指针与数组

先看例子

#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}

可以看出arr地址即为首元素地址,当然两种情况除外。(数组章节有说明哦).

既然arr可以当作指针,那么就有以下的操作:

#include <stdio.h>
int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,0};
    int *p = arr; //指针存放数组首元素的地址
    int sz = sizeof(arr)/sizeof(arr[0]);
    for(i=0; i<sz; i++)
   {
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p+i);
   }
    return 0;
}


可以看到, p+i 其实计算的是数组 arr 下标为i的地址,那么我们可以通过指针来访问数组元素。

int main()
{
 int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
 int *p = arr; //指针存放数组首元素的地址
 int sz = sizeof(arr) / sizeof(arr[0]);
 int i = 0;
 for (i = 0; i<sz; i++)
 {
 printf("%d ", *(p + i));
 }
 return 0;
}


二级指针

指针变量也是变量,是变量就有地址。如int *p是一个指针变量,那么它的地址就由二级指针来存储


可以看到,a是整型变量,p是一级指针,解引用后为10,pp是二级指针,*pp表示一级指针的地址,**pp表示解一级指针的指向的内容。

野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。

所以,我们要规避野指针的情况。

1.创建指针记得初始化,当不知道指向哪里时,直接指向NULL,不能不管它;

2.不能使访问位置越界,如一个数组有10个元素,你访问到下标为10的位置,超过数组长度范围,这就是访问越界。

3. 指针指向空间释放,及时置NULL;

4. 避免返回局部变量的地址,局部变量的指针指向的地址会在函数走完后销毁,当返回到主函数时,主函数指针接收不到任何地址。

5. 指针使用之前检查有效性。

相关文章
|
21天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
74 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
21天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
46 9
|
21天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
40 7
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
105 13
|
24天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
24天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
83 3
|
25天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
24天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
36 1
|
28天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
28天前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。