首页 > 其他分享 >C语言中二维数组和数组名的二意性

C语言中二维数组和数组名的二意性

时间:2024-05-24 21:27:51浏览次数:21  
标签:10 arr 二意性 int C语言 ++ 数组名 printf array

1. 二维数组

二维数组的本质,也是一维数组,一维数组中的每个元素,又是一个一维数组
声明/定义:int[4] array[3] => int array[3][4];

int main()
{
  int a[3][4];
  printf("&arr[0][0] = %p\n", &a[0][0]); // 0x16f38b2e8
  printf("&arr[0] = %p\n", &a[0]);       // 0x16f38b2e8
  printf("&a = %p\n", &a);               // 0x16f38b2e8
  printf("arr[0] = %p\n", a[0]);         // 0x16f38b2e8
  printf("arr = %p, arr+1 = %p\n", arr, arr + 1);  // 加的是4*4=16个字节
  printf("arr[0] = %p arr[0] + 1 = %p\n", arr[0], arr[0] + 1);  // 加的是4个字节
  return 0;
}

打印的地址都是相同的,可以打印二维数组中任何单个元素的值,但当打印数组名(如 a 或 a[0])时,您得到的是地址,因为数组名在大多数情况下会被转换为指向数组首元素的指针。

1.1. 二维数组的定义中:行可以省,列不可以省,列里面有类型信息

int main()
{
  int a[][4] = {{1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4}};
  for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
  {
    for (int j = 0; j < 4; j++)
    {
      printf("a[%d][%d] = %4d\t", i, j, a[i][j]);
    }
  }

  return 0;
}

1.2. 存储

二维数组是行列矩阵,他在内存中存储的是一维的,按行顺序存储
在这里插入图片描述

1.3. 数组的三要素

在这里插入图片描述

1.4. 联系

1. 主对角线与次对角线输出

输入一个4*4的二维数组,并输出该数组的主对角线和次对角线上的元素
在这里插入图片描述

int main()
{
  int array[4][4] = {{1, 1, 1, 1}, {2, 2, 2, 2}, {3, 3, 3, 3}, {4, 4, 4, 4}};
  for (int i = 0; i < 4; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      if (i == j)
      {
        printf("%d\t", array[i][j]);
      }
    }
    putchar(10);
  }

  for (int j = 3; j >= 0; j--)
  {
    printf("%d\t", array[3 - j][j]);
  }
  return 0;
}

主对角线规律:行和列的索引值是相同的i==j
次对角线规律:行的索引值+列的索引值=3

2. 逆置一个二维数组

将一个矩阵4*4进行转置处理,要求初始化原始矩阵,输出原矩阵和转置后的矩阵
在这里插入图片描述

int main()
{
  char arr[4][4] = {'a','b','b','b','c','a','b','b','c','c','a','b','c','c','c','a'};
  char t;
  for (int i = 0; i < 4; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      printf("%2c\t", arr[i][j]);
    }
    putchar(10);
  }

  for (int i = 0; i < 4; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      // printf("%d%d\t", i, j);
      if (i > j)
      {
        t = arr[i][j];
        arr[i][j] = arr[j][i];
        arr[j][i] = t;
      }
    }
    putchar(10);
  }

  for (int i = 0; i < 4; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      printf("%2c\t", arr[i][j]);
    }
    putchar(10);
  }
  return 0;
}

思路:由于转置后的矩阵是对称的,所以只有下三角部分的元素需要交换。因此,通过判断i > j来确定是否需要交换元素。交换操作是通过一个临时变量t来完成的。

3. 天生棋局

生成一个10*10的棋局,要求,初始化为零。随机置入10颗棋子,棋子处置为1,并打印。
在这里插入图片描述
若在棋子中出现连续三个棋子在一起,包含横行和竖行,则输出好棋,否则,输出臭棋
在这里插入图片描述

int main()
{
  int chess[10][10] = {0};
  srand(time(NULL));
  int x, y;
  // int count = 10;
  // while (count--)
  // {
  //   // 问题:如果有相同的值,则不满10个数
  //   x = rand() % 10;
  //   y = rand() % 10;
  //   chess[x][y] = 1;
  // }

  int count = 0;
  // 可
  // while (1)
  // {
  //   x = rand() % 10;
  //   y = rand() % 10;
  //   if (chess[x][y] != 1)
  //   {
  //     chess[x][y] = 1;
  //     count++;
  //     if (count == 10)
  //       break;
  //   }
  // }

  // 亦可
  while (count < 20)
  {
    x = rand() % 10;
    y = rand() % 10;
    if (chess[x][y] == 1)
      continue;
    chess[x][y] = 1;
    count++;
  }

  // 打印出棋盘
  for (int i = 0; i < 10; i++)
  {
    for (int j = 0; j < 10; j++)
    {
      printf("%d ", chess[i][j]);
    }
    putchar(10);
  }

  count = 0;
  int flagGoodChess = 0;
  // 判断好棋:逐行逐列扫描
  for (int i = 0; i < 10; i++) // 横行扫描
  {
    count = 0;
    for (int j = 0; j < 10; j++)
    {
      if (chess[i][j] == 1)
      {
        count++;
        if (count == 3)
        {
          flagGoodChess = 1;
          break;
        }
      }
      else
        count = 0;
    }
    if (flagGoodChess == 1)
      break;

    count = 0;
    for (int j = 0; j < 10; j++) // 竖行扫描
    {
      if (chess[j][i] == 1)
      {
        count++;
        if (count == 3)
        {
          flagGoodChess = 1;
          break;
        }
      }
      else
        count = 0;
    }
    if (flagGoodChess == 1)
      break;
  }

  if (flagGoodChess == 1)
    printf("好棋\n");
  else
    printf("臭棋\n");
  return 0;
}

思路:

  1. 棋子放置:在一个无限循环中,随机选择一个位置,如果该位置没有棋子,则放置一个棋子,并将计数器加1。当放置了10个棋子后,跳出循环。这种方法确保了每个棋子都是唯一放置的。
  2. 检查好棋:
    1. 代码通过两个嵌套循环检查每一行和每一列,看是否有连续三个位置都被标记为1。
    2. 如果在某一行或列找到了连续三个1,则将flagGoodChess标记为1,并跳出循环。
    3. 最后,根据flagGoodChess的值打印出结果:如果是1,则打印“好棋”;否则打印“不好棋”。

4. 有序数组归并

合并两个已经有序的数组A[M],B[M],到另外一个数组C[M+N]中,使用另外一个数组依然有序。其中M和N均是宏常量。
在这里插入图片描述

#define M 5
#define N 3
int main8()
{
  int A[M] = {1, 3, 5, 7, 9};
  int B[N] = {2, 4, 6};
  int C[M + N];

  int i = 0, j = 0, k = 0;
  while (i < M && j < N)
  {
    if (A[i] < B[j])
      C[k++] = A[i++]; // 被放进去的,索引也要往前走
    else
      C[k++] = B[j++];
  }

  // A数组走完了
  while (i < 5)
    C[k++] = A[i++];
  // B数组走完了
  while (j < 3)
    C[k++] = B[j++];

  for (int i = 0; i < M + N; i++)
  {
    printf("%d\n", C[i]);
  }
  return 0;
}

思路:

  1. 合并过程:使用了一个while循环来比较数组A和B中的元素。如果A中的当前元素小于B中的当前元素,则将A的元素添加到C中,并将i和k的值增加;否则,将B的元素添加到C中,并将j和k的值增加。这个过程一直持续到其中一个数组的元素全部被添加到C中。
  2. 处理剩余元素:在while循环结束后,可能会有一个数组还有剩余元素没有被添加到C中。两个单独的while循环,检查哪个数组已经遍历完毕,然后继续将另一个数组的剩余元素添加到C中。这两个循环分别在i < M和j < N的情况下将剩余元素添加到C中。

2. 数组名的二义性

数组名,是数组的唯一标识符,既表示一种构造数据类型的大小,也表示访问 组中的成员的首地址,用来访问数据成员使用

2.1. 一维数组名

一维数组名,他是一种构造类型,同时他又承担了访问每个数组元素的责任,所以数组名就有两重性。

// 数组名是数组的唯一标识符
// 数组名充当一种构造类型
// 数组名,充当访问数据成员的首地址
int main9()
{
  int arr[10] = {2};
  printf("sizeof(arr) = %d\n", sizeof(arr));        // 40
  printf("sizeof(int[10] = %d\n", sizeof(int[10])); // 40

  printf("arr = %p\n", arr);     // 0x16fcc3330
  printf("arr = %p\n", &arr[0]); // 0x16fcc3330

  printf("arr[0] = %d\n", *(arr + 0)); // 2
  return 0;
}

2.2. 二维数组名

二维数组本质是一种嵌套关系,本质是一种一维数组,每个一维数组成员又是一个一维数组,

int main()
{
  int array[3][4];
  printf("sizeof(array) = %d sizeof(int[3][4]) = %d\n", sizeof(array), sizeof(int[3][4])); // 48 48
  printf("sizeof int[4] = %d sizeof(array[0] = %d\n", sizeof(int[4]), sizeof(array[0]));   // 16 16
  printf("array = %p &array[0] = %p\n", array, &array[0]);                                 // 0x16f2632e8 0x16f2632e8
  printf("array + 1 = %p &array[0] + 1 = %p\n", array + 1, &array[0] + 1);                 // 0x16f2632f8 0x16f2632f8
  printf("array[0] = %p &array[0][0] = %p\n", array[0], &array[0][0]);                     // 0x16f2632e8 0x16f2632e8
  printf("array[0] + 1 = %p &array[0][0] + 1 = %p\n", array[0] + 1, &array[0][0] + 1);     // 0x16f2632ec  0x16f2632ec
  return 0;
}

printf(“array + 1 = %p &array[0] + 1 = %p\n”, array + 1, &array[0] + 1); 都是指向下一行的起始地址
printf(“array[0] + 1 = %p &array[0][0] + 1 = %p\n”, array[0] + 1, &array[0][0] + 1); 都是指向第一行的第二个元素。

// 一维数组的逻辑和存储是一致的,均是线性的
// 二维数组的逻辑是二维的,但其存储是线性的
// 数组在内存中是一段连续的存储空间
// 存储是线性的是由内存物理特性决定的
int main()
{
  int a[3][4] = {1, 2, 3};
  printf("sizeof(a) = %d\n", sizeof(a));                 // 48
  printf("sizeof(int[3][4]) = %d\n", sizeof(int[3][4])); // 48
  printf("a = %p\n", a);                                 // 0x16d3ef2e8
  printf("&a = %p\n", &a[0]);                            // 0x16d3ef2e8
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      printf("%d \t", *(a[i] + j)); // 1       2       3       0
                                    // 0       0       0       0
                                    // 0       0       0       0
    }
    putchar(10);
  }
  return 0;
}
  • *操作符:
    • 解引用:用于获取指针指向的地址处的值(例如:*ptr)。
    • 声明指针:用于声明一个指针变量(例如:*int ptr)。
  • &操作符:
    • 取地址:用于获取变量的地址(例如:&x)。
    • 获取表达式结果的地址:例如获取数组元素的地址(例如:&a[0])。

标签:10,arr,二意性,int,C语言,++,数组名,printf,array
From: https://blog.csdn.net/qq_36816794/article/details/139156895

相关文章

  • 实验5 C语言指针应用编程
    1.实验任务1task1_1.c1#include<stdio.h>2#defineN534voidinput(intx[],intn);5voidoutput(intx[],intn);6voidfind_min_max(intx[],intn,int*pmin,int*pmax);78intmain()9{10inta[N];11intmin,max;1213......
  • 【c语言】一篇文章搞懂函数递归
    ......
  • 48.C语言初阶指针整理
    先来简单的了解指针可以说地址即是指针通过指针(地址)能找到以它为地址的内存单元存放于指针中的值都被当成地址处理一个简单的指针inta=10;int*p=&a;inta=1;int*pa=&a;char*pc=&a;//取出地址一样printf("%p\n",pa);printf("......
  • C语言----深入理解指针(3)
    1.字符指针变量//intmain()//{//charch='w';//char*pc=&ch;//printf("%c",*pc);//return0;//}/*intmain(){//char*p="abcdef";//chararr[]="abcdef";//常量字符串abcdef\0//......
  • C语言---试计算在区间1 到n 的所有整数中,数字x(0 ≤ x ≤ 9)共出现了多少次?
    #include<stdio.h>intmain(){intn,x;scanf("%d%d",&n,&x);intcount=0;for(inti=1;i<=n;i++){intm=i;//从1开始计算while(m)//循环运行的条件{if(m%10==x)//如果m除以10的余数是x的......
  • 【C语言】文件操作
    在C语言中,文件操作是通过一组标准库函数来实现的,这些函数定义在<stdio.h>头文件中。以下是一些基本的文件操作函数及其用法:文件打开(fopen):FILE*fopen(constchar*filename,constchar*mode);filename是要打开的文件名。mode是打开文件的方式,例如"r"(只读)、"w"(只......
  • 【C语言】字符函数和字符串函数
    在C语言中,字符函数和字符串函数通常用于处理单个字符和字符串。这些函数定义在<ctype.h>(字符函数)和<string.h>(字符串函数)头文件中。以下是一些常用的字符函数和字符串函数及其用法:字符函数(ctype.h)字符函数主要用于测试字符的属性或转换字符。isalnum():检查字符是......
  • STL 以及 C语言与C++的区别
    C++语言的标准库(STL)容器(Containers)vector:动态数组,可以动态增长和收缩,支持快速随机访问元素。list:双向链表,支持在任意位置快速插入和删除元素。map:关联容器,以键值对(key-value)形式存储元素,支持快速查找和插入。竞赛常用C++STL用法https://io.zouht.com/154.htmlv......
  • C语言 - 结构体转cha*
    c语言结构体转cha*在C语言中,将结构体转换为char*通常意味着你想要获取结构体的内存表示,并将其视为字符数组。这种转换可以通过使用memcpy函数来实现。下面是一个简单的例子,展示了如何将结构体转换为char*:#include<stdio.h>#include<stdlib.h>#include<string.h>......
  • 47.C语言函数练习题整理
    题目来自练习册和牛客网的一些编程题目整理函数都有返回值且只有一个返回值声明类型为void可以返回空值若调用一个函数中没有return语句返回一个不确定的值形参是动态变量实参和形参之间的数据传递方式为实参到形参的单向值传递形参的值发生改变不会影响主调函数中的......