C语言指针的创建与使用

一、如何理解指针?

不论是什么语言,当变量声明之后,电脑将分配一块内存给该变量进行存储,那么学过计算机组成原理的话我们就可以知道,当电脑需要调用该变量时,是通过CPU的寄存器里面保存的地址来进行“寻址”操作。

我们可以做一个简单的类比,将内存看作一个庞大的连续别墅群,每一个别墅都有一个特定的编号。假设每一个别墅存储的信息量为一个Byte,别墅群能够存储的总信息量为4GB(2^32Bytes),那么我们就可以按照这个标准对每一栋别墅进行编号,从0x00000001到0xFFFFFFFF(也就是十进制的1~4294967296)。

CPU可以看作这一整个别墅群的运作管理中心,目前的CPU大部分都是以4Bytes(32bits)为一个存储单位,可以类比为,一个家庭拥有4栋别墅来进行工作。但是呢,在我们的C语言中,并不是所有变量都只有32bits(intchar类型的数据是32bit),也有一些变量需要多于4Bytes。

由于我们需要保证数据的连续性,所以一个数据大概率存储在相邻的“别墅”中,所以当我们需要找到某部分特定数据,就需要通过地址信息找到第一栋别墅然后按照顺序去读取“别墅中存储的信息。

在C语言中,这个地址信息就被称作指针 !用来找到对应的数据块。

由于不同的数据类型有不同的编码解码方式以及大小, 所以当我们声明指针时,也需要明确指针指向的数据类型。

//int类型的指针
int* int_point;

//声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向 int 类型对象的指针
int* arr[10]

// 声明一个数组指针,该指针指向一个 int 类型的一维数组   
int (*arr)[10] 

// 声明一个指针 p ,该指针指向一个 int 类型的指针
int **p; 
      
//char类型的指针
char * char_point;

//结构类型的指针
struct name * struct_point;

在声明时加入“*”运算符表示该变量存储的是指针,也就是地址。

这里需要强调几个点:

(1)<int* arr[10]>: arr里面存储的都是指针,每一个指针都指向一个int类型的数据。

   <int (*arr)[10]>: arr这个变量存储的是某个长度为10的数组的指针,也就是数组第一个元素的地址。

(2)<int **p>: 指针同样也可以被看作是一个存储在内存中的变量,因此同样也可以为他们提供指针进行寻找。

(3)如果p是一个指针,保存一个地址,该地址指向内存中的某个变量,“*p”可以访问该指针对应的变量。

二、指针的初始化

/* 方法1:使指针指向现有的内存 */
int x = 1;
int *p = &x;  
// 指针 p 被初始化,指向变量 x ,其中取地址符 & 用于产生操作数内存地址

/* 方法2:动态分配内存给指针 */
int *p;
p = (int *)malloc(sizeof(int) * 10);    
// malloc 函数用于动态分配内存
free(p);    
// free 函数用于释放一块已经分配的内存,常与 malloc 函数一起使用,要使用这两个函数需要头文件 stdlib.h

指针的初始化便是为指针赋值一个合法的地址,让程序知道这个指针指向哪里。

三、未初始化和非法的指针

在C语言的编辑过程中,我们经常会遇见“ Segmentation fault(core dumped)” 

这个原因大部分是因为非法指针的存在,举个例子:
#include "stdio.h"

int main(){
    int *p;
    *p = 1;
    printf("%d\n",*p);

    return 0;  
}
这个程序编译能通过,但是运行会报错,因为指针p并没有指向任何存储变量的地址。
需要进行如下更改:
#include "stdio.h"

int main(){
    int x = 1;  
    int *p = &x;
    printf("%d\n",*p);
   *p = 2;
    printf("%d\n",*p);

    return 0;  
}

四、NULL指针

NULL指针表示不指向任何对象。

#include "stdio.h"

int main(){
    int *p = NULL;
    printf("p的地址为%p\n",p);

    return 0;
}

/***************
 * 程序输出:
 * p的地址为(nil)
***************/

五、指针的运算

  1. 指针+/-整数

p++指向的是p的下一个内存地址。这种运算并不会改变p自己的地址,而仅仅是改变指向的地址。

  1. 指针-指针

只有当指针指向同一个数组里的元素时才能进行这样的运算,计算出来的结果是两个指针之间的操作数的数量。

#include "stdio.h"

int main(){
    int a[10] = {1,2,3,4,5,6,7,8,9,0};
    int sub;
    int *p1 = &a[2];
    int *p2 = &a[8];

    sub = p2-p1;                                                                            
    printf("%d\n",sub);    // 输出结果为 6

    return 0;
}

六、指针与数组

  在C语言中,通过指针对数组进行操作极大地提高了数组操作的效率,相较于Java等面向对象等语言,更加直接。

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
//arr是该数组第一个元素的地址
int *p;
//两种给指针赋值的方式:
//1.&是得到变量地址的运算符
p = &arr[0];
//2.直接用arr进行赋值
p = arr; 

数组指针的操作:

#include "stdio.h"
 
 int main(){
     int arr[2][3] = {1,2,3,4,5,6};               // 定义一个二维数组并初始化
     int (*p)[3];                                 // 定义一个数组指针,指针指向一个含有3个元素的一维数组
 
     p = arr;                                     // 将二维数组的首地址赋给 p,此时 p 指向 arr[0] 或 &arr[0][0]
     printf("%d\n",(*p)[0]);              // 输出结果为 1
     p++;                         // 对 p 进行算术运算,此时 p 将指向二维数组的下一行的首地址,即 &arr[1][0]
     printf("%d\n",(*p)[1]);                      // 输出结果为5
 
     return 0;                                                                               
 }

七、指针与结构

结构的两种声明方式:

struct message{            // 声明一个结构 message
    char name[10];             // 成员
    int age;
    int score;  
};

typedef struct message s_message;     // 类型定义符 typedef

s_message mess = {"tongye",23,83};    // 声明一个 struct message 类型的变量 mess,并对其进行初始化 
--------------------------------------------------------------------------------------------------------------
/* 另一种更简便的声明方法 */
typedef struct{
  char name[10];
  int age;
  int score;
}message;

对上面的结构可以定义结构指针,并通过结构指针来操作结构内容

s_message *p;        // 声明一个结构指针 p ,该指针指向一个 s_message 类型的结构
*p = &mess;      // 对结构指针的初始化与普通指针一样,也是使用取地址符 &

#include "stdio.h"

typedef struct{
    char name[10];
    int age;
    int score;  
}message;

int main(){
    message mess = {"tongye",23,83};
    message *p = &mess;

    printf("%s\n",p->name);      // 输出结果为:tongye
    printf("%d\n",p->score);         // 输出结果为:83

    return 0;
}

八、指针与函数

C语言的所有参数均是以“传值调用”的方式进行传递的,这意味着函数将获得参数值的一份拷贝。这样,函数可以放心修改这个拷贝值,而不必担心会修改调用程序实际传递给它的参数。

指针可以作为函数的参数进行传递,相较于直接传递变量,这种方式的好处是,不需要对传递的变量进行更改,而仅仅只需要最指针所对应的内存空间的值进行更改,无需更改指针。

#include "stdio.h"

void swap1(int a,int b)        // 参数为普通的 int 变量
{
  int temp;
  temp = a;
  a = b;
  b = temp;
}

void swap2(int *a,int *b)      // 参数为指针,接受调用函数传递过来的变量地址作为参数,对所指地址处的内容进行操作
{
  int temp;                    // 最终结果是,地址本身并没有改变,但是这一地址所对应的内存段中的内容发生了变化,即x,y的值发生了变化
  temp = *a;
  *a = *b;
  *b = temp;
}

int main()
{
  int x = 1,y = 2;
  swap1(x,y);                     // 将 x,y 的值本身作为参数传递给了被调函数
  printf("%d %5d\n",x,y);         // 输出结果为:1     2

  swap(&x,&y);                    // 将 x,y 的地址作为参数传递给了被调函数,传递过去的也是一个值,与传值调用不冲突
  printf("%d %5d\n",x,y);         // 输出结果为:2     1
  return 0;
}

然后呢,可以给函数添加一个指针,该指针指向函数的入口地址,这样的指针和其他指针的操作并无二异。

#include "stdio.h"
#include "string.h"

int str_comp(const char *m,const char *n);                             // 声明一个函数 str_comp,该函数有两个 const char 类型的指针,函数的返回值为 int 类型
void comp(char *a,char *b,int (*prr)(const char *,const char*));       // 声明一个函数 comp ,注意该函数的第三个参数,是一个函数指针

int main()
{
    char str1[20];      // 声明一个字符数组
    char str2[20];
    int (*p)(const char *,const char *) = str_comp;            // 声明并初始化一个函数指针,该指针所指向的函数有两个 const char 类型的指针,且返回值为 int 类型
    gets(str1);         // 使用 gets() 函数从 I/O 读取一行字符串
    gets(str2);
    comp(str1,str2,p);  // 函数指针 p 作为参数传给 comp 函数

    return 0;
}

int str_comp(const char *m,const char *n)
{
   // 库函数 strcmp 用于比较两个字符串,其原型是: int strcmp(const char *s1,const char *s2);
    if(strcmp(m,n) == 0) 
        return 0;
    else
        return 1;
}

/* 函数 comp 接受一个函数指针作为它的第三个参数 */
void comp(char *a,char *b,int (*prr)(const char *,const char*))
{
    if((*prr)(a,b) == 0)
        printf("str1 = str2\n");
    else
        printf("str1 != str2\n");
}

“(*p)”中的括号不能省略,否则将表示p为函数,返回一个类型为“int*”的值。

本文鸣谢博客:

tongye的博客

代码来源于上述博客,转载或使用请说明

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值