C语言数组

目录

一维数组

二维数组

 传递数组给函数

方式 1

方式 2

方式 3

实例

二维数组传递给函数

方法1: 第一维的长度可以不指定,但必须指定第二维的长度:

方法2: 指向一个有5个元素一维数组的指针:

方法3: 利用数组是顺序存储的特性,通过降维来访问原数组!

实例

从函数返回数组


概述: 数组是一种数据结构,用于存储相同类型的元素数组名实际上是一个指向其第一个元素的指针

一维数组

声明数组:

要声明一个数组,你需要指定数组的类型、数组的名称以及数组的大小。例如:

int arr[5]; // 声明一个名为arr的整型数组,大小为5

初始化数组:

1. 在声明数组的同时初始化它:

int arr[5] = {1, 2, 3, 4, 5}; // 初始化一个整型数组

2. 只给部分元素赋值,只给前面部分元素赋值,其余未赋值的默认赋0。

int a[10]={12, 19, 22 , 993, 344};

int b[10] = {0}; // 所有元素都为0

组名本身是一个常量指针,意味着它的值是不能被改变的,一旦确定,就不能再指向其他地方。 使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。

double balance[10];
// balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。
// 因此,下面的程序片段把 p 赋值为 balance 的第一个元素的地址:
double *p;
double balance[10];

p = balance;

一旦把第一个元素的地址存储在 p 中,就可以使用 p、(p+1)、*(p+2) 等来访问数组元素。

int array = [1,2,3];		    // 定义数组

array[0] == *array == 1;		// 数组名表示数组第一个元素地址
array[1] == *(array + 1) == 2;	// 加一表示第二个元素地址
array[2] == *(array + 2) == 3;	// 加二表示第三个元素地址

获取数组长度

数组长度可以使用 sizeof 运算符来获取数组的长度,例如:

int numbers[] = {1, 2, 3, 4, 5};
int length = sizeof(numbers) / sizeof(numbers[0]);
// 数组的总字节数 / 数组第一个元素的字节 = 数组的长度

在 C 语言中,数组名表示数组首元素的地址。当我们在声明和定义一个数组时,该数组名就代表着该数组的地址。

int myArray[5] = {10, 20, 30, 40, 50};
int *ptr = &myArray[0]; // 或者直接写作 int *ptr = myArray;

在上面的例子中,ptr 指针变量被初始化为 myArray 的地址,即数组的第一个元素的地址。

需要注意的是,虽然数组名表示数组的地址,但在大多数情况下,数组名会自动转换为指向数组首元素的指针。这意味着我们可以直接将数组名用于指针运算,例如在函数传递参数或遍历数组时:

void printArray(int arr[], int size) // 等价于 int *arr
{
    for (int i = 0; i < size; i++)
    {
        printf("%d ", arr[i]); // 数组名arr被当作指针使用
    }
}

int main()
{
    int myArray[5] = {10, 20, 30, 40, 50};
    printArray(myArray, 5); // 将数组名传递给函数 地址传递
    return 0;
}

在上述代码中,printArray 函数接受一个整数数组和数组大小作为参数,我们将 myArray 数组名传递给函数,函数内部可以像使用指针一样使用 arr 数组名。

字符数组

字符数组通常用于存储字符串。字符串在C语言中通常以空字符'\0'结尾。例如:

char str[] = "Hello"; // 声明并初始化一个字符数组(字符串)
// 会在后面自动补上 /0

使用sizeof来计算str数组的大小(以字节为单位)会返回6,因为整个数组包括一个额外的字符'\0'来表示字符串的结束。

如果你要计算字符数组(字符串)中的元素个数,可以使用sizeof(str) /sizeof(str[0]),也会返回6,因为每个字符占用一个字节,而整个数组有6个字符(包括结束符'\0')。 


二维数组

多维数组最简单的形式是二维数组。一个二维数组,在本质上,是一个一维数组的列表。

声明一个 x 行 y 列的二维整型数组,形式如下:

type arrayName [ x ][ y ];

其中,type 可以是任意有效的 C 数据类型,arrayName 是一个有效的 C 标识符。一个二维数组可以被认为是一个带有 x 行和 y 列的表格。下面是一个二维数组,包含 3 行和 4 列:

int x[3][4];

int a[3][4] = {  
 {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
 {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
 {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};

因此,数组中的每个元素是使用形式为 a[ i , j ] 的元素名称来标识的,其中 a 是数组名称,i 和 j 是唯一标识 a 中每个元素的下标。

int a[3][4] = {{0}};

C语言数组_第1张图片

二维数组存放字符串,读取时当一维数组使用。比如:

#include 

int main() 
{
    char names[3][10] = {"John", "Mark", "Sarah"};
    for (int i = 0; i < 3; i++) 
    {
        printf("Name: %s\n", names[i]);
    }
    
    return 0;
}

结果

Name: John
Name: Mark
Name: Sarah

 传递数组给函数

方式 1

形式参数是一个指针:优先使用此方法

void myFunction(int *param) 
{ 
    . . . 
}

方式 2

形式参数是一个已定义大小的数组: 若传递的数组大于形参定义的大小时,编译器不会报警。但是在函数内部,我们应该小心使用 param,以确保我们不会超出定义的大小访问数组元素。如果 myFunction 尝试访问超出定义大小的元素,那将导致未定义行为。

void myFunction(int param[10])
{ 
	. . . 
}

方式 3

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

void myFunction(int param[]) 
{ 
    . . . 
}

实例

#include 
 
/* 函数声明 */
double getAverage(int arr[], int size);
 
int main ()
{
   /* 带有 5 个元素的整型数组 */
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;
 
   /* 传递一个指向数组的指针作为参数 */
   avg = getAverage( balance, 5 ) ;
 
   /* 输出返回值 */
   printf( "平均值是: %f ", avg );
    
   return 0;
}
 
double getAverage(int arr[], int size)
{
  int    i;
  double avg;
  double sum=0;
 
  for (i = 0; i < size; ++i)
  {
    sum += arr[i];
  }
 
  avg = sum / size;
 
  return avg;
}

结果

平均值是: 214.400000

二维数组传递给函数

方法1: 第一维的长度可以不指定,但必须指定第二维的长度:

void print_a(int a[][5], int n, int m) //n,m 为数组的行与列

方法2: 指向一个有5个元素一维数组的指针:

void print_b(int (*a)[5], int n, int m) //n,m 为数组的行与列

方法3: 利用数组是顺序存储的特性,通过降维来访问原数组!

void print_c(int *a, int n, int m) //n,m 为数组的行与列

实例

#include 
/*********************************
* 方法1: 第一维的长度可以不指定
*        但必须指定第二维的长度
*********************************/ 
void print_a(int a[][5], int n, int m){ 
    int i, j;
    for(i = 0; i < n; i++) {
        for(j = 0; j < m; j++) 
            printf("%d ", a[i][j]); 
        printf("\n"); 
    } 
} 

/***************************************** 
* 方法2: 指向一个有5个元素一维数组的指针
*****************************************/ 
void print_b(int (*a)[5], int n, int m) { 
    int i, j;
    for(i = 0; i < n; i++) { 
        for(j = 0; j < m; j++) 
            printf("%d ", a[i][j]);
        printf("\n"); 
    } 
}

/*********************************** 
* 方法3: 利用数组是顺序存储的特性, 
*       通过降维来访问原数组!
***********************************/ 
void print_c(int *a, int n, int m) { 
    int i, j; 
    for(i = 0; i < n; i++) { 
        for(j = 0; j < m; j++) 
            printf("%d ", *(a + i*m + j));
        printf("\n"); 
    } 
}
int main(void) 
{ 

    int a[5][5] = {{1, 2}, {3, 4, 5}, {6}, {7}, {0, 8}}; 
	// 注意数组并未全部赋初值 为赋初值的元素默认为0
	// 1   2   0   0   0
	// 3   4   5   0   0
	// 6   0   0   0   0
	// 7   0   0   0   0
	// 0   8   0   0   0
    

    printf("\n方法1:\n");   
    print_a(a, 5, 5); 

    printf("\n方法2:\n");   
    print_b(a, 5, 5);   

    printf("\n方法3:\n");   
    print_c(&a[0][0], 5, 5); //传递第一个元素的地址

    return 0; 
} 

数组用作函数入参时,最好带上其长度

sizeof求数组的长度结果时 ,不能在入参后求,也不能在入参时求。因为数组形参此时被默认转换为指针类型,此时求得的结果为指针类型的空间大小。

// 在函数参数声明中,使用了数组形式的参数 int a[],但实际上在函数内部,参数 a 被默认转换为指针类型 int*。
// 因此,对于 sizeof(a) 的求值,实际上是对指针类型的大小求值,而不是数组的大小。


//		下面代码编译器报错但可分析         //

void getSize(int a[])	//这里int a[] 可替换为int *a效果一样
{
  int i;
  i=sizeof(a);// 这里的i是4,是int *的大小,不管你传入的a有几个元素
  int b;
  b=sizeof(a)/sizeof(a[0]);// 逻辑没错,a的大小除以元素的大小得到长度,可是sizeof(a)并不是数组占的空间,而是指针,也就是i
}

int main()
{
  int a[]={1,2,3,4,5};
  getSize(a);	// 传递数组名 即数组地址
}

//
//			正确做法应在传递前 把数组长度计算出来			 	//
/
void getSize(int a[],int size)
{

  int k = size;// 已经保存入参大小
    
  			  // 遍历或者其他操作

}

int main()
{
  int a[]={1,2,3,4,5};
    
   // 此时a并不是入参,sizeof可以得到开发人员想要的结果
  int size = sizeof(a)/sizeof(a[0]);
    
  getSize(a,size);
}


使用约定一个结束标记的方法来确定数组的元素个数如下

// 在定义数组时,在数组的最后一个元素后面添加一个特殊的结束标记,例如 -1 或 0。
// 假设有一个整型数组 arr,可以定义为 int arr[] = {1, 2, 3, 4, 5, -1};。
// 在函数内部,使用循环来遍历数组。当遇到结束标记时,停止循环。

void getSize(int a[]) 
{
    int i = 0;
    while (a[i] != -1) 	// 当遇到 -1时循环条件不满足 跳出循环
    {
        i++;
    }
  // 现在,变量 i 的值就是数组的元素个数
  printf("数组的元素个数:%d\n", i);
}


从函数返回数组

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

因为当函数执行完毕后,局部变量被销毁并释放其内存空间,因此不能保证在函数返回后局部变量的地址仍然有效。具体来说,当你在一个 C 函数内部声明一个局部变量时,该变量只在函数执行期间存在,并在函数结束时被销毁。这意味着你不能在函数外部使用该变量的地址或引用。

若要在外部引用则要定义为static变量。

int* getLocalVariableAddress()
{
    int x = 10;  // 在函数内部声明的局部变量
    return &x;   // 错误:尝试返回局部变量的地址
}

int main() 
{
    int* ptr = getLocalVariableAddress();  // 错误:尝试使用函数内部局部变量的地址
    // ...
    return 0;
}

C 语言不允许返回一个完整的数组作为函数的参数。但是,可以通过指定不带索引的数组名来返回一个指向数组的指针。

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

int * myFunction()	// 此函数可返回指针
{
    ....
}
#include 
#include 
#include 
 
/* 要生成和返回随机数的函数 */
int * getRandom( )
{
  static int  r[10];// 定义静态数组  若不定义为静态则在执行完函数销毁 因此不能返回地址
  int i;		   // 定义循环变量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;
}

结果

r[0] = 313959809
r[1] = 1759055877
r[2] = 1113101911
r[3] = 2133832223
r[4] = 2073354073
r[5] = 167288147
r[6] = 1827471542
r[7] = 834791014
r[8] = 1901409888
r[9] = 1990469526
*(p + 0) : 313959809
*(p + 1) : 1759055877
*(p + 2) : 1113101911
*(p + 3) : 2133832223
*(p + 4) : 2073354073
*(p + 5) : 167288147
*(p + 6) : 1827471542
*(p + 7) : 834791014
*(p + 8) : 1901409888
*(p + 9) : 1990469526

上一篇:C语言运算符

下一篇: 

你可能感兴趣的:(C语言随笔,c语言,开发语言)