C语言回顾深入学习:数组与指针

C数组

数组是用来存储一系列相同类型的变量的顺序集合。所有的数组都是由连续的内存位置组成。

声明数组

需要指定元素的类型和元素的数量

type arrayName [ arraySize ];

初始化数组

//大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//省略掉了数组的大小,数组的大小则为初始化时元素的个数。
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//为数组中某个元素赋值
balance[4] = 50.0;

数组元素可以通过数组名称加索引进行访问。

多维数组

多维数组声明的一般形式如下:

type name[size1][size2]...[sizeN];

二维数组,在本质上,是一个一维数组的列表,其声明及初始化:

int a[3][4] = {  
 {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
 {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
 {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};
//内部嵌套的括号是可选的,下面的初始化与上面是等同的
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

传递数组给函数

要在函数中传递一个数组作为参数,必须以下面三种方式来声明函数形式参数。这三种声明方式的结果是一样的,因为每种方式都会告诉编译器将要接收一个整型指针。

1. 形式参数是一个指针:

void myFunction(int *param) 
{ 
body of function
 }

2. 形式参数是一个已定义大小的数组:

void myFunction(int param[]) 
{ 
body of function
 }

3. 形式参数是一个未定义大小的数组:

void myFunction(int *param) 
{ 
body of function
 }

从函数返回数组

要从函数返回一个一维数组,必须声明一个返回指针的函数,:

int * myFunction()
{
 body of function
}

C 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。

示例:

/*
从函数返回一个由10个随机数构成的数组
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
/* 要生成和返回随机数的函数 */
int * getRandom( )
{
  static int  r[10];
  int i;
 
  /* 设置种子 */
  srand( (unsigned)time( NULL ) );
  for ( i = 0; i < 10; ++i)
  {
     r[i] = rand();
     printf( "r[%d] = %d\n", i, r[i]);
 
  }
 
  return r;
}
 
/* 要调用上面定义函数的主函数 */
int main ()
{
   /* 一个指向整数的指针 */
   int *p;
   int i;
 
   p = getRandom();
   for ( i = 0; i < 10; i++ )
   {
       printf( "*(p + %d) : %d\n", i, *(p + i));
   }
 
   return 0;
}

指向数组的指针

数组名是一个指向数组中第一个元素的常量指针。

double *p;
double balance[10];

p = balance;

balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。 把 p 赋值为 balance 的第一个元素的地址。使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。

注意:

1. *p+4 是指 p 是指向balance数组的首地址(&balance[0])的指针(&表示取地址),*p是对首地址的元素取值(*在赋值符号右边和表达式中表示取值),该值加4即为 balance[0] +4 

2. *(p+4)  是指 p 是指向balance数组的首地址(&balance[0])的指针(&表示取地址) ,  p+4表示地址加上4,*再对p+4取值,所以,*(p+4) 表示 balance[4] (第5个元素的值)。

C枚举

枚举是 C 语言中的一种基本数据类型。枚举语法定义格式为:

enum enumName {enum1,enum2,……};

可以在定义枚举类型时改变枚举元素的值,没有指定值的枚举元素,其值为前一元素加 1。第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。

枚举变量的定义

有三种方式来定义枚举变量

1. 先定义枚举类型,再定义枚举变量

enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;

2. 定义枚举类型的同时定义枚举变量

enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;

3, 省略枚举名称,直接定义枚举变量

enum
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;

在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的,所以按照 C 语言规范是没有办法遍历枚举类型的。不过在一些特殊的情况下,枚举类型连续的值连续时是可以实现有条件的遍历。

示例(使用 for 来遍历枚举的元素):

#include<stdio.h>
 
enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
int main()
{
    // 遍历枚举元素
    for (day = MON; day <= SUN; day++) {
        printf("枚举元素:%d \n", day);
    }
}

枚举类型不连续,这种枚举无法遍历,示例:

enum
{
    ENUM_0,
    ENUM_10 = 10,
    ENUM_11
};

C 指针

学习 C 语言的指针,是很有必要的。它不但可以简化任务,而且比如进行动态内存分配,没有指针是无法执行的。变量都有内存位置,变量都定义了可以使用连字号(&)运算符访问的地址。

什么是指针?

指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。所以,指针的值是它所指向变量的地址。

指针变量声明的一般形式为:

type *var-name;

所有指针的值的类型都是一样的,都是一个代表内存地址的十六进制数。指针所指向的变量或常量的数据类型不同。

如何使用指针?

1. 定义一个指针变量

2. 把变量地址赋值给指针

3. 访问指针变量中可用地址的值。

这些是通过使用一元运算符 *来返回位于操作数所指地址的变量的值。示例如下:

#include <stdio.h>
 
int main ()
{
   int  var = 20;   /* 实际变量的声明 */
   int  *ip;        /* 指针变量的声明 */
 
   ip = &var;  /* 在指针变量中存储 var 的地址 */

   /*取变量地址使用& */
   printf("Address of var variable: %p\n", &var  );
 
   /* 在指针变量中存储的地址 */
   printf("Address stored in ip variable: %p\n", ip );
 
   /* 使用指针访问值(*是取对应内存地址的值) */
   printf("Value of *ip variable: %d\n", *ip );
 
   return 0;
}

C 中的 NULL 指针

声明指针变量的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针,空指针指向的地址是0x0。

空指针是一个定义在标准库中的值为零的常量。在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。

然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针是空值(零值(Null)),则假定它不指向任何东西。

指针的算术运算

C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

ptr++

 假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,ptr 每增加一次,它都将指向下一个整数位置,如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

递增或递减变量指针

访问数组中的每一元素:

/*指针递增遍历*/
#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int  i, *ptr;
 
   /* 指针中的数组地址 */
   ptr = var;
   for ( i = 0; i < MAX; i++)
   {
 
      printf("存储地址:var[%d] = %x\n", i, ptr );
      printf("存储值:var[%d] = %d\n", i, *ptr );
 
      /* 移动到下一个位置 */
      ptr++;
   }
   return 0;
}

/*指针递减遍历*/
#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int  i, *ptr;
 
   /* 指针中最后一个元素的地址 */
   ptr = &var[MAX-1];
   for ( i = MAX; i > 0; i--)
   {
 
      printf("存储地址:var[%d] = %x\n", i-1, ptr );
      printf("存储值:var[%d] = %d\n", i-1, *ptr );
 
      /* 移动到下一个位置 */
      ptr--;
   }
   return 0;
}

指针的比较

指针可以用关系运算符进行比较,如 ==、< 和 >。指针直接比较是地址大小比较。需要比较指针所指向变量的值可以使用*号取值之后再进行比较。修改上述递增访问数组元素的代码,变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],就把变量指针进行递增,如下:

#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int  i, *ptr;
 
   /* 指针中第一个元素的地址 */
   ptr = var;
   i = 0;
   while ( ptr <= &var[MAX - 1] )
   {
 
      printf("Address of var[%d] = %x\n", i, ptr );
      printf("Value of var[%d] = %d\n", i, *ptr );
 
      /* 指向上一个位置 */
      ptr++;
      i++;
   }
   return 0;
}

指针数组

指针数组的声明:

type *pointerArrayName[size];

//例如,声明一个由10个指向整数的指针构成的指针数组:
/*int *ptr[10];*/

下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:

#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int i, *ptr[MAX];
 
   for ( i = 0; i < MAX; i++)
   {
      ptr[i] = &var[i]; /* 赋值为整数的地址 */
   }
   for ( i = 0; i < MAX; i++)
   {
      //ptr[i]是指针数组中的指针,是整数的地址;*对指针指向整数的地址取出值
      printf("Value of var[%d] = %d\n", i, *ptr[i] );
   }
   return 0;
}

也可以用一个指向字符的指针数组来存储一个字符串列表,如下:(有点疑问的,后续研究)

#include <stdio.h>
 
const int MAX = 4;
 
int main ()
{
   const char *names[] = {
                   "Zara Ali",
                   "Hina Ali",
                   "Nuha Ali",
                   "Sara Ali",
   };
   int i = 0;
 
   for ( i = 0; i < MAX; i++)
   {
      //names[i]是指针数值*names[]中的元素,是字符指针,names[i]所指向的变量的地址存放的值是字符串的首字母
      printf("Value of names[%d] = %s\n", i, names[i] );
   }
   return 0;
}

指向指针的指针

一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号:

type **pointerName;

当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符:

#include <stdio.h>
 
int main ()
{
   int  var,*ptr, **pptr;
   var = 3000;
   /* 获取 var 的地址 */
   ptr = &var;
   /* 使用运算符 & 获取 指向变量的指针ptr 的地址 */
   pptr = &ptr;
   printf("Value of var = %d\n", var );
   printf("Value available at *ptr = %d\n", *ptr );
   /* 使用 **pptr 获取值 */
   printf("Value available at **pptr = %d\n", **pptr);
   return 0;
}

传递指针给函数

C 语言允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。

#include <stdio.h>
#include <time.h>
 
void getSeconds(unsigned long *par);

//也可以将数组名作为指针传递给函数,数组名就是数组的首地址。

int main ()
{
   unsigned long sec;
   getSeconds( &sec );//传递指针给函数,是传递的地址
   /* 输出实际值 */
   printf("Number of seconds: %ld\n", sec );
   return 0;
}
void getSeconds(unsigned long *par)
{
   /* 获取当前的秒数 */
   *par = time( NULL );//par指针指向的sec地址存储的值赋值为当前时间秒数
   return;
}

从函数返回指针

C 允许您从函数返回指针。为此,必须声明一个返回指针的函数,如下所示:

int * myFunction()
{
body of function
}

另外,C 语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量。:

因为局部变量是存储在内存的栈区内,当函数调用结束后,局部变量所占的内存地址便被释放了,因此当其函数执行完毕后,函数内的变量便不再拥有那个内存地址,所以不能返回其指针。

除非将其变量定义为 static 变量,static 变量的值存放在内存中的静态数据区,不会随着函数执行的结束而被清除,故能返回其地址。

发布了161 篇原创文章 · 获赞 90 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_42415326/article/details/104027363