带你深层了解c语言指针

简介: 带你深层了解c语言指针

一、字符指针


我们可以定义一个字符指针,指向一个字符变量,并通过指针来修改这个字符变量.


#include <stdio.h>
int main()
{
  char a = 'x';
  char* p = &a;
  *p = 'y';//通过指针来修改
  printf("%c\n", *p);
  printf("%c", a);
  return 0;
}


那么字符指针还可以怎么用呢?


下面这段代码的运行结果是什么呢?


int main()
{
  char* s1 =  "aabbccdd" ;
  for (int i = 0; i < 3; i++)
  {
    *(s1 + i) = '0';
  }
  printf("%s", s1);
  return 0;
}


答案:


运行错误,原因是"aabbccdd"是常量字符串,它们存放在"常量区"(里面的东西是只读的),是不允许被修改的.


分析:


这段代码是将常量字符串存放进s1指针里面了吗?


显然不可能,想放也放不进去呀.


正确理解是:将该常量字符串的首元素放进了s1,即s1指向该字符串的首元素.


const关键字在后续的库函数模拟中会详细介绍,这里可以理解为被修饰的字符指针指向的内容,使其不能被修改.


使用字符指针打印字符串


int main()
{
  const char* s1 =  "aabbccdd" ;
  printf("%s", s1);
  return 0;
}


如果对上面的代码还是不理解可以看一下,下面的这一道曾经的笔试题:


#include <stdio.h>
int main()
{
  char str1[] = "你好,初阶牛!.";
  char str2[] = "你好,初阶牛!";
  const char* str3 = "你好,初阶牛!";
  const char* str4 = "你好,初阶牛!";
  if (str1 == str2)
    printf("str1 and str2 are same\n");
  else
    printf("str1 and str2 are not same\n");
  if (str3 == str4)
    printf("str3 and str4 are same\n");
  else
    printf("str3 and str4 are not same\n");
  return 0;
}


答案:


str1 and str2 are not same


str3 and str4 are same


原因分析:


str1和str2是两个的字符数组,两个数组是相互独立的,它们在创建时,会各自向内存申请空间,所以地址必然不一样,只不过是在内存中两块不同内存区域存放着相同的内容罢了.


由于字符串常量是不能修改的,在内存中这类常量并没有必要保存两份,他们存储在内存中的常量区(只读型)所以str3和str4指向的是同一块空间.


图解:



二、指针数组


1.1 指针数组的定义


指针数组是数组还是指针?


答案是数组.


我们知道,


整形数组:存放整形的数组.


字符数组,:存放字符的数组.


浮点型数组:存放浮点型数据的数组,


  int arr1[] = { 1,2,3,4,5 };//整形数组
  char arrr2[] = "abcdef";//字符型数组
  double arr3[] = { 3.4,5.8,1.9 };//浮点型数组


那么用于存放指针的数组自然被称为指针数组了.


那么下面哪个是指针数组?


  int* arr1[10];
  int(*arr2)[10];


由于" * “'(星号)的优先级要低于”[ ]"(中括号)


arr1指针数组: arr1先与[10]先结合,故arr1是指针数组


arr2数组指针: 由于括号的优先级更高,所以(*arr2)是指针,指向的对象是数组,故arr2是数组指针.


常见的指针数组:


int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组


2.2 使用指针数组模拟二维数组


#include <stdio.h>
int main()
{
  int arr1[4] = { 1,2,3,4 };
  int arr2[4] = { 2,3,4,5 };
  int arr3[4] = { 3,4,5,6 };
  int* arr[3] = { arr1,arr2,arr3 };
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      printf("%d ", *(arr[i] + j));
    }
    printf("\n");
  }
  return 0;
}


arr1数组,arr2数组,arr3数组在内存中都有自己独立的内存空间,


我们将这三个一维数组的首元素地址放在一个新的指针的数组(arr)中,通过指针数组来访问这三个一维数组,效果就如同二维数组一样,但并不是真正的二维数组.


图解:



内存中存储的图:



三、数组指针


指向数组的指针被称为数组指针.


#include <stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
  int(*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
  //指针p就是数组指针,指向arr数组
  return 0;
}


数组指针对于一维数组作用并不是很大,数组指针一般用于二维数组.


在二维数组传参时可以使用数组指针接收.


对于初学者,数组指针和指针数组经常容易搞混.


试着分析下面这些代码的含义吧!


int arr[5];//1
int *arr1[10];//2
int (*arr2)[10];//3
int (*arr3[10])[5];//4


答案:


1.整形数组:一个包含5个整形元素的整形数组


2.指针数组:数组中包含十个元素,每个元素都是一个整形指针(int*).


3.数组指针:指向一个拥有十个元素的整形指针.


4.数组指针数组:arr3先与[10]结合,说明arr3是一个数组,


其次剩下的int(*)[5]是arr3数组中的元素,显然是数组的每个元素都是一个数组指针.故arr3是存放了10个指向有5个整形元素数组的数组指针数组.


四、一维数组的传参


4.1 整形一维数组传参:


整形一维数组传过来的arr的是数组首元素地址,即一个整形的地址.


整形变量的地址用一级整形指针接收或者同样用数组接收都行.


void test1(int arr[])//使用不指定具体大小的一维数组接收
{}
void test2(int arr[10])//使用具体大小的一维数组接收
{}
void test3(int *arr)//使用指针接收
{}


具体实现:


#include <stdio.h>
//使用同样一维数组接收
void test1(int arr[],int sz)//arr1
{
  for (int i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
}
//与test1一样的道理,用指定大小接收当然也没问题
void test2(int arr[5],int sz)//arr1
{}
//使用指针接收
void test3(int* arr,int sz)//arr1
{
  for (int i = 0; i < sz; i++)
  {
    printf("%d ", *(arr+i));//等价于printf("%d ", arr[i]);
  }
  printf("\n");
}
int main()
{
  int arr1[] = { 1,2,3,4,5 };
  int sz = sizeof(arr1) / sizeof(arr1[0]);
  test1(arr1,sz);
  test2(arr1,sz);
  test3(arr1,sz);
  return 0;
}


4.2 一维指针数组传参


这三种有一种是错误的.


void test4(int *arr[20])
{}
void test5(int* arr)
{}
void test6(int **arr)
{}


一维指针数组的数组名表示首元素的地址,而首元素是一个整形指针,一级(整形)指针的地址可以用二级指针接收.也可以用相同类型的一维指针数组接收.


#include <stdio.h>
//用整形指针数组接收整形指针数组
void test4(int* arr[3])
{
  for (int i = 0; i < 3; i++)//决定访问整形指针的个数
  {
    for (int j = 0; j < 4; j++)//决定使用整形指针向后访问整形元素的个数
    {
      printf("%d ", *(arr[i] + j));
    ///等价于printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
  printf("\n");
}
//错误,不能使用
void test5(int* arr)
{
}
//用二级指针接收一维指针数组
void test6(int** arr)
{
  for (int i = 0; i < 3; i++)//决定访问整形指针的个数
  {
    for (int j = 0; j < 4; j++)//决定使用整形指针向后访问整形元素的个数
    {
      printf("%d ", *(*(arr+i) + j));//这种一般不好理解
      //等价于 printf("%d ", *(arr[i] + j));
      //等价于 printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
}
int main()
{
  int arr1[4] = { 1,2,3,4 };
  int arr2[4] = { 2,3,4,5 };
  int arr3[4] = { 3,4,5,6 };
  int* arr[3] = { arr1,arr2,arr3 };
  test4(arr);
  test5(arr);
  test6(arr);
}


五、二维数组传参


给定一个二维整形数组:


二维数组的数组名代表首元素地址,即第一行的地址(int*[ ]);


int arr[2][4] = { {1,2,3,4 },{5,6,7,8} };


下面七种传参方式,哪些是正确的.


void test1(int arr[2][4])
void test2(int arr[][])
void test3(int arr[][4])
void test4(int *arr)
void test5(int* arr[4])
void test6(int (*arr)[4])
void test7(int **arr)


正确传参方式:test1()、test3()和test6()


#include <stdio.h>
void test1(int arr[2][4])
{
  int i = 0, j = 0;
  printf("test1\n");
  for (i = 0; i < 2; i++)
  {
    for (j = 0; j < 4; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
  printf("\n");
}
void test3(int arr[][4])
{
  int i = 0, j = 0;
  printf("test2\n");
  for (i = 0; i < 2; i++)
  {
    for (j = 0; j < 4; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
  printf("\n");
}
void test6(int(*arr)[4])//ok?
{
  int i = 0, j = 0;
  printf("test3\n");
  for (i = 0; i < 2; i++)
  {
    for (j = 0; j < 4; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
  printf("\n");
}
int main()
{
  int arr[2][4] = { {1,2,3,4 },{5,6,7,8} };
  test1(arr);
  test3(arr);
  test6(arr);
}


二级指针作为参数


二级指针可以接收一级指针的地址.


#include <stdio.h>
void print1(int** aa)
{
  printf("%d\n", **aa);
}
void print2(char** pp)
{
  printf("%c\n", **pp);
}
int main()
{
  int a = 5;
  int* aa = &a;
  char* p = "abcdef";
  char** pp = &p;
  print1(&aa);
  print2(&p);
  print2(pp);
  return 0;
}


目录
相关文章
|
2月前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
62 0
|
25天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
77 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
25天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
49 9
|
25天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
42 7
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
126 13
|
28天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
28天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
102 3
|
29天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
1月前
|
存储 C语言 开发者
C 语言指针与内存管理
C语言中的指针与内存管理是编程的核心概念。指针用于存储变量的内存地址,实现数据的间接访问和操作;内存管理涉及动态分配(如malloc、free函数)和释放内存,确保程序高效运行并避免内存泄漏。掌握这两者对于编写高质量的C语言程序至关重要。
58 11
|
28天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
39 1