C语言数组

  数组作用:可以用来保存很多记录(可以看成一种大容器)。一些简单游戏也基本由数组实现,如游戏地图(二维数组)等等。
  一个数组 划分 多个单元(下标区分) -存放-> 多个同类元素

1. 数组定义与初始化

1.1. 定义: <类型> 变量名称[元素数量];
在main函数外先定义全局数组变量,如: int a[10005]; 默认初始化为0
注意点:
(1)元素数量必须是整数,C99之前元素数量必须是编译时刻确定的字面量!而C99这里可以支持变量。MSVC编译器不完全支持C99,VS2022用cpp才能这样写,而且还是不支持scanf入一个变量作为数组长度!

(EOF参见C语言学习笔记06结尾部分。)EOF -1 ctrl+Z
不过,DevCpp在使用变量定义数组时也不支持同时做初始化操作。

(2)数组一旦创建不能改变大小(C99变量被赋值后创建的数组大小也是根据变量值确定的),内部元素的内存排列是依次连续的,元素类型都一样(与数组类型一致)。
(3)arr[10]的下标是0~9(arr[0] ~ arr[9],一个数组单元就是一个变量)。【C和C-like语言的一大特点,从0开始数】需要注意下标的有效范围(遍历时让循环变量i从0到<数组长度,这样循环体内i最大时是数组最大有效下标),不要越界segmentation fault 段错误(概率导致程序bug,指针有关)。

1.2. 初始化:

DevCpp中,static修饰或作为全局变量时的数组会被默认初始化为0,在main中定义时则默认随机值。

(1)利用for循环对数组初始化。
int a[10];
for (int i = 0; i < 10; i++) {
a[i] = 0;
}
上面的也等价于:
int a[10] = {0};

(2)集成初始化-直接依次赋值。
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, }; //数组共10个元素,最后有无‘,’均可。
int a[10] = {3}; //数组共10个元素,第一个是3,后面全是0
C99标准还支持给数组指定位置赋值:
int a[13] = {[1] = 2, 4, [5] = 6}; //没有赋值的地方取0,值4赋给下标为2的数组单元。
即 0 2 4 0 0 6 0 0 0 0 0 0 0 。
int a[] = {[5] = 6}; //因涉及到最大下标5,所以共6个元素。

(3)使用memset函数初始化数组(或结构体)
头文件 string.h
memset(arr, 0, sizeof(arr));
该函数 按照字节赋值sizeof(类型) * 元素个数 ,注意sizeof(指针)可能会造成错误(指针和数组名不完全相同!)。一般只用 memset函数整型赋值0或-1,按字节就是全0或全f,赋其它值( 如:1 )未必得到预期结果。

给结构体初始化:
memset( & 结构体类型变量名, 0, sizeof(struct 结构体名));

(4)用数组变量给数组变量赋值(数组赋给数组),只能用for遍历数组元素进行赋值。
int a[] = {1, 2, 3};
int b[length];
for (i = 0; i < length; i ++) {
b[i] = a[i];
}

 int a[] = {1,2,3};int length = sizeof(a)/sizeof(a[0]);int b[sizeof(a)/sizeof(a[0])] = {0};printf("%d %d %d\n", sizeof(a), sizeof(b), length);for ( int i = 0; i < sizeof(b)/sizeof(b[0]); i ++ ) {printf("%d\n", b[i]);}for ( int i = 0; i < length; i ++ ) {b[i] = a[i];}for ( int i = 0; i < sizeof(b)/sizeof(b[0]); i ++ ) {printf("%d\n", b[i]);}

1.3. 计算数组长度
sizeof会给出所占据的字节数
数组单元个数 = sizeof(arr)/sizeof(arr[0])

使用场景:数组作为函数参数时,往往必须用另一个参数来传入数组大小,因为不能在a[]中给出数组大小而无法在函数内再利用sizeof来计算元素个数

2. 字符数组、字符串、字符串数组(关于指针可以先看C语言学习笔记10指针)

2.1. 概念

字符数组:形如:char word[] = {‘H’, ‘e’, ‘l’, ‘l’,‘o’,‘!’}; //6B大小
字符数组不是C语言的字符串,不能用字符串的方式做计算。(C语言没有string类型这样显式的字符串)
C语言的字符串可以用字符数组实现 —— char word[] = {‘H’, ‘e’, ‘l’, ‘l’,‘o’,‘!’,‘\0’}; //在字符数组元素最后加上’\0’或0,7B大小(数组长度),但计算字符串长度时0不会被记录,0只表示字符串的结束。【int的0是0x30,即ascii码的48。】

2.2. 字符串定义与使用

字符串变量定义:char * str = “Hello”; //使用双引号 编译器自动添0,占1B
通常指针形式定义的字符串位于低地址区域,不作为本地变量,只可读不可写(良好的操作系统的保护机制有关),因此我们用数组形式定义字符串:
       char word[] = “Hello”; // char line[10] = “Hello!”;

字符串以数组形式存在,通常用数组形式定义字符串而以指针/数组形式访问遍历。(C因为字符串是数组,所以不能用运算符对字符串运算,而java和python中可以对字符串简单计算)
头文件string.h中有许多处理字符串的函数。

C语言学习笔记04中printf输出字符串,这里就说明了两个相邻字符串会被自动连接成一个大字符串

它也能改写成如下,注意下行要顶格,否则会带空格

补充说明:下面s和s2指向的地址是一样的且不是本地变量,如果要对 s[0] = ‘X’; 是不可行的;原因是char类型指针指向字符串常量时本质是一个const char * 的常量,编译器因历史原因接收不加const,但试图改变该常量不行:


下面这样写,s和s2地址不一样,s数组是本地变量,s[0]可以被修改:

2.3. 字符的输入输出

单字符输入输出函数——int putchar(int)、int getchar(void)【int型的单个字符,出错返回EOF(-1)】
Shell服务程序:行编辑- ctrl+Z / ctrl+D (ctrl+C结束程序)】

getchar一个个读入。
scanf按格式符读入,使用%7s提高了安全性:

char * gets(char * ) 和 int puts(const char * ):
gets可以避免在输入缓冲区buffer中遗留\n,但是输入可以超过定义长度,不安全(而原数组截断到定义的长度)。【s初始化10个null】
puts成功返回非负值,失败返回EOF。

字符串常见错误:
使用指针定义 char * s; 字符串时,没有指向一个有效地址(需要有效的初始化)就直接使用,此时易导致程序出错。

2.4. 字符串数组

main函数参数:

字符串数组形式介绍(最后的 * s[] 应是存放指针的数组,内部数据不可修改):

月份英文单词输出练习 ——

const char * monthWords (int *p) {const char *s[] = {"Wrong Number","January","February","March","April","May","June","July","August","September","October","November","December"};if ( *p >= 1 && *p <= 12 ) {printf("%d月的英文单词:", *p);}else {*p = 0;}return s[*p];
}int main() {printf("请输入月份:");int month;scanf("%d", &month);printf("%s\n", monthWords(&month));
//  switch (month) {//      case 1: printf("January\n"); break;
//      case 2: printf("February\n"); break;
//      case 3: printf("March\n"); break;
//      case 4: printf("April\n"); break;
//      case 5: printf("May\n"); break;
//      case 6: printf("June\n"); break;
//      case 7: printf("July\n"); break;
//      case 8: printf("August\n"); break;
//      case 9: printf("September\n"); break;
//      case 10: printf("October\n"); break;
//      case 11: printf("November\n"); break;
//      case 12: printf("December\n"); break;
//  }return 0;
}


2.5. string.h中提供的字符串函数

各C发行版一般已经在string.h中定义好了如下常用函数:

strlen:返回字符串长度,不包含\0的结尾

strcmp:比较字符串大小,相等返回0,不相等返回首个不相等处字符的地址之差大于0返回1小于0返回-1)【数组的比较是地址的比较,所以永远是false】
int strncmp:可以只判断前几个字符是否相等 —— (s1,s2,n)

strcpy:复制字符串,strcpy(dst, src),dst和src是不可重叠的( * restrict C99),将src拷贝到dst,返回dst。【复制 char * dst = (char * )malloc(strlen(src) + 1); strcpy(dst, src); 】
strcat:strcat(dst_s1, src_s2),把s2拷贝到s1后面,形成一个长字符串,返回s1
建议使用安全版本:strncpy strncat —— (dst, src,n),拷贝字符个数

自己实现以上函数功能:

int mystrlen(const char *s) { //strlenint idx = 0;while ( s[idx] != '\0' ) {idx ++;}return idx;
}
int mystrcmp(const char *s1, const char *s2) { //strcmp//数组方式实现 定义一个下标变量//int idx = 0;//while ( s1[idx] == s2[idx] && s1[idx] != '\0' ) {//      if ( s1[idx] != s2[idx] ) {//          break;
//      }
//      else if ( s1[idx] == '\0') {//          break;
//      }//idx ++;//}//return s1[idx]-s2[idx];//指针方式实现while ( *s1 == *s2 && *s1 != '\0' ) {s1 ++;s2 ++;}return *s1 - *s2;
}
char * mystrcpy(const char *src, char *dst) { //strcpy(dst, src) 源宿相反//数组方式
//  int idx = 0;
//  while ( src[idx] ) {//      dst[idx] = src[idx]; //destination source
//      idx ++;
//  }
//  dst[idx] = '\0';
//  return dst;//指针方式char *ret = dst;while ( *src ) { // *src != '\0'*dst++ = *src++;}*dst = '\0';return ret; //返回结果可以继续参与运算
}
char * mystrcat(char *dst, const char *src) { //strcatchar *ret = dst;while ( *dst ) {*dst ++;}while (*dst++ = *src++) ;return ret;
}

char * strchr(str, int c):在字符串中从左向右(strrchr 从右开始)找字符,返回(NULL) 没找到,只找第一个满足的【要继续找后面的:char * p = strchr(str, ‘a’); p = strchr(p+1, ‘a’);】
strstr:在字符串中找一个字符串,strcasestr:寻找过程忽略大小写


关于strchr函数的小套路(如何找第二个相同的字符,保留相同字符后的字符串输出):

保留相同字符之前的字符串输出:

char s[] = "hello";
char *p = strchr(s, 'l');
char c = *p;
*p = '\0';
char *t = (char *)malloc(strlen(s) + 1);
strcpy(t, s);
*p = c; //恢复s
printf("%s %s\n", t, s);
free(t);

3. 二维数组

通常可以将二维数组理解为矩阵——int a [3][5] 是一个整型的3行5列(根据其在内存中的排列考量)的矩阵,初始化二维数组时必须给出列数,如:
int a[][5] = {{0,1,2,3}, {2,3,4,5,6,}, }; (行数会由编译器来数,此处是2行;未明确初始化赋值的单元补0;C99也可以用定位[i][j]=n)。
如果每行不加{} (int a[][5] = {0,1,2,3,4,2,3}; ),就表示从左上角到右下角逐行(自左向右)填充值。

二维数组的遍历借助两层for循环,a[i][j]表示第 i 行第 j 列上的单元(同样从0开始数) 。
a[i,j]是会先计算表达式 i,j 得到 j ,即a[j] ,它不能正确表达一个二维数组的单元,是错误的。

游戏1:井字棋 tic-tac-toe 展示部分代码

判断输赢(4条线):

const int size = 3; // #define SIZE 3int board[size][size]; //少用magic魔法数/飞来数,尽量用可以标识的变量int i, j;int numOfX, numOfO; // 1-X    0-Oint result = -1; // 1 X win    0 O win    -1 no win//check rowfor ( i = 0; i < size && result == -1; i ++ ) {numOfO = numOfX = 0;for ( j = 0; j < size; j ++ ) {if ( board[i][j] == 1 ) {numOfX ++;}else {numOfO ++;}}if ( numOfO == size ) {result = 0;}else if ( numOfX == size ) {result = 1;}}//check columnif ( result == -1 ) {for ( j = 0; j < size && result == -1; j ++ ) {numOfO = numOfX = 0;for ( i = 0; i < size; i ++ ) {if ( board[i][j] == 1 ) {numOfX ++;}else {numOfO ++;}}if ( numOfO == size ) {result = 0;}else if ( numOfX == size ) {result = 1;}}}//check diagonal line +-numOfO = numOfX = 0;for ( i = 0; i < size; i ++ ) {if ( board[i][i] == 1 ) {numOfX ++;}else {numOfO ++;}}if ( numOfO == size ) {result = 0;}else if ( numOfX == size ) {result = 1;}numOfO = numOfX = 0;for ( i = 0; i < size; i ++ ) {if ( board[i][size-i-1] == 1 ) {numOfX ++;}else {numOfO ++;}}if ( numOfO == size ) {result = 0;}else if ( numOfX == size ) {result = 1;}

游戏2:控制台打飞机 展示部分代码

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>/*
***打飞机游戏设计***
*///宏定义界面尺寸
#define Width 36 //宽36列
#define Height 24 //高24行//宏定义敌人数量
#define EnemyNum 5//全局变量声明与定义
int canvas[Height][Width] = { 0 };//用二维数组记录界面中对应的元素,初始化为0。不需要具体定义攻击波位置了,也不用考虑未发射攻击波情况-1。
//二维数组内的数据含义:0空格,1角色*,2攻击波|(二维数组实现了连续输出攻击波),3敌人&
int pos_x, pos_y;//角色位置坐标
int enemy_x[EnemyNum], enemy_y[EnemyNum];//敌人位置坐标
int EXP;//角色获得的经验值,注:exp报错built-in function可能是因为exp是标准库函数名
int EnemyNSpeed;//敌人新移速值
int attk_big;//大攻击波(宽度变宽)//特殊库函数声明
//<windows.h>中的函数gotoxy:作用是将光标移至(x,y)处,可以代替system("cls");清屏
void gotoxy(int x, int y);
//<windows.h>中的函数HideCursor:作用是隐藏光标
void HideCursor();//自定义函数声明
void Init();//界面数据初始化
void Show();//界面展示
void UpdateWithoutInput();//无输入更新
void UpdateWithInput();//有输入更新//***入口函数***
int main()
{system("color FA");//背景-亮白色F,前景-淡绿色AInit();//首先初始化数据//不断循环操作执行过程while (1) {Show();//界面展示UpdateWithoutInput();//先更新与用户输入无关的部分UpdateWithInput();//再更新与用户输入有关的部分}return 0;
}//有输入更新函数定义
void UpdateWithInput() {char input;//定义字符变量input存放输入的字符//当按键时执行,vs里必须加_if (_kbhit()) {//无回车瞬时读取输入的字符,vs里必须加_input = _getch();//上w、下s、左a、右d方向移动if (input == 'w') {canvas[pos_y][pos_x] = 0;//原位置先归零pos_y--;//角色运动//不能出上边界if (pos_y < 0) {pos_y = 0;}canvas[pos_y][pos_x] = 1;//新位置显示}if (input == 's') {canvas[pos_y][pos_x] = 0;pos_y++;//不能出下边界if (pos_y > Height - 1) {pos_y = Height - 1;}canvas[pos_y][pos_x] = 1;}if (input == 'a') {canvas[pos_y][pos_x] = 0;pos_x--;//不能出左边界if (pos_x < 0) {pos_x = 0;}canvas[pos_y][pos_x] = 1;}if (input == 'd') {canvas[pos_y][pos_x] = 0;pos_x++;//不能出右边界if (pos_x > Width - 1) {pos_x = Width - 1;}canvas[pos_y][pos_x] = 1;}if (input == ' ') {//攻击状态int left, right;//定义左右攻击扩展半径left = pos_x - attk_big;if (left < 0) {//防止攻击波范围越界后在屏上循环显示left = 0;}right = pos_x + attk_big;if (right > Width - 1) {right = Width - 1;}int i;//记录攻击波发射列for (i = left; i <= right; i++) {canvas[pos_y - 1][i] = 2;//攻击波发射于角色的上一行,且与发射位置同列}}//按esc键暂停,按任意键恢复。esc键的ascaii码值为27if (input == 0x1b) {system("pause");//强制等待命令暂停程序}}
}

更高维的,如三维、四维数组和二维数组类似考虑和想象。

C语言学习笔记09-数组、字符数组、字符串数组、二维数组(单字符输入输出putchar、getchar,字符串输入输出的scanf、gets、puts)相关推荐

  1. C语言求二维数组平均数,一道JavaScript的二维数组求平均数的题

    JavaScript中只支持一维数组,但是可以在数组中嵌套数组来创建二维以至于多维的数组.今天下午在看书时候,发现一道感觉比较有意思的题,就是js中如何求二维数组的列之和和行之和,现在就给大家分享下, ...

  2. C语言基础入门48篇_30_二维数组的定义与使用(二维数组的定义:type 数组名[行][列]、二维数组的初始化、二维数组的引用)

    1. 二维数组的定义 type 数组名[行][列] 2. 二维数组的初始化 2.1 全部初始化为0 char chAry[2][3] = { 0 }; 实例: #include <stdio.h ...

  3. php 二维数组中去重,PHP实现二维数组去重功能示例

    PHP实现二维数组去重功能示例 发布于 2017-08-07 21:05:17 | 68 次阅读 | 评论: 0 | 来源: 网友投递 PHP开源脚本语言PHP(外文名: Hypertext Prep ...

  4. 【java进阶06:数组】使用一维数组模拟栈数据结构 使用二维数组模拟酒店,酒店管理系统 Arrays工具类 冒泡排序算法、选择排序算法、二分法

    目录 数组 二维数组 总结 作业 Arrays工具类 数组 数组总结 及 静态初始化一维数组 /* Array:1.java语言中的数组是一种引用数据类型,不属于基本数据类型,数组的父类是Object ...

  5. 二维数组和指针(包含交换二维数组行列)

    有必要对a[i]的性质作进一步说明.a[i]从形式上看是a数组中的第i个元素.如果a是一维数组名,则a[i]代表a数组第i个元素所占的内存单元的内容.a[i]是有物理地址的,是占内存单元的.但如果a是 ...

  6. php 二维数组根据键值合并二维数组_php数组实现根据某个键值将相同键值合并生成新二维数组的方法详解...

    这篇文章主要介绍了php数组实现根据某个键值将相同键值合并生成新二维数组的方法,涉及php数组的遍历.赋值相关运算技巧,需要的朋友可以参考下 本文实例讲述了php数组实现根据某个键值将相同键值合并生成 ...

  7. python 二维数组心得_基于python 二维数组及画图的实例详解

    python中如何使用二维数组 在Python中,一个像这样的多维表格可以通过"序列的序列"实现.一个表格是行的序列.每一行又是独立单元格的序列.这类似于小编们使用的数学记号,在数 ...

  8. php某列为键数组为值,PHP 将二维数组中某列值作为数组的键名 -- 超实用

    有时候,想通过数组的中某字段值, 然后再在二维数组中获取存在该字段值的数组: 一般能想到的就是foreach 遍历比较一下跟该字段值一样,就获取到想要的数组,如下: //测试二维数组 $arr =ar ...

  9. php 二维数组根据键值合并二维数组_php数组根据某键值,把相同键值的合并最终生成一个新的二维数组...

    匿名用户 1级 2013-12-29 回答 php数组根据某一个键值,把相同键值的合并生成一个新的二维数组 源数据: $infos = array( array( 'a' => 36, 'b' ...

  10. Java黑皮书课后题第8章:8.29(相同的数组)如果两个二维数组m1和m2具有相同的内容,则它们是相同的。编写一个方法,如果m1和m2相同的话,返回true

    8.29(相同的数组)如果两个二维数组m1和m2具有相同的内容,则它们是相同的.编写一个方法,如果m1和m2相同的话,返回true 题目 题目描述与运行示例 破题 代码 题目 题目描述与运行示例 8. ...

最新文章

  1. python迭代器生成器使用技巧(2):切片、遍历、索引值、多序列、多容器对象
  2. Oracle 大规模 delete,update 操作 注意事项
  3. c++ string截取字符串_String类的常见用法
  4. html5画布视屏,HTML5视频、音频和画布
  5. 马云的经典语录(转载)
  6. [51nod]2128 前缀异或【数学题】
  7. PaddleHub创意项目 | 将霉霉P到埃菲尔铁塔前
  8. Longest Increasing Subsequence
  9. 【愚公系列】2022年04月 密码学攻击-RSA之共模和模不互素
  10. 世界人工智能大会阿里巴巴专场论坛《数字时代的技术责任》来了
  11. 加州大学圣地亚哥计算机硕士申请,美国加州大学圣地亚哥分校计算机工程硕士.pdf...
  12. 第4关 注册配置中心实现
  13. 数据缓存层及相关知识
  14. 公众号选题方向有哪些?
  15. CRM- Salesforce体验报告
  16. 缓存与分布式锁——场景实现
  17. AI教程之 Stable Diffusion在自己电脑上运行稳定的AI自动艺术创作
  18. matlab中wav转txt6,WAV转TXT专家下载
  19. 算法 algorithms
  20. Python 是前浪,Julia 是后浪?

热门文章

  1. Excel·VBA多级联动的数据有效性
  2. uniApp正则表达式校验手机、邮箱
  3. php欢迎界面代码,分享微信小程序欢迎界面开发的实例代码
  4. switch语句的ns图怎么画_visio画ns图.doc
  5. 飞腾cpu服务器浪潮信息,推动产业进程 浪潮发国产飞腾CPU服务器
  6. Element UI 表格单选、多选情景
  7. 北航软件测试与质量管理,北航软件学院质量与测试专业.doc
  8. 使用javax.mail发送邮件详解
  9. 简易QQ聊天代码及分析
  10. IE 阻止过期的 ActiveX 控件