赞
踩
数组作用:可以用来保存很多记录(可以看成一种大容器)。一些简单游戏也基本由数组实现,如游戏地图(二维数组)等等。
一个数组 划分 多个单元(下标区分) -存放-> 多个同类元素
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来计算元素个数。
字符数组:形如: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。】
字符串变量定义: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]可以被修改:
单字符输入输出函数——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; 字符串时,没有指向一个有效地址(需要有效的初始化)就直接使用,此时易导致程序出错。
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;
}
各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) { //strlen
int 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) { //strcat
char *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);
通常可以将二维数组理解为矩阵——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] ,它不能正确表达一个二维数组的单元,是错误的。
判断输赢(4条线):
const int size = 3; // #define SIZE 3
int board[size][size]; //少用magic魔法数/飞来数,尽量用可以标识的变量
int i, j;
int numOfX, numOfO; // 1-X 0-O
int result = -1; // 1 X win 0 O win -1 no win
//check row
for ( 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 column
if ( 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;
}
#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,前景-淡绿色A
Init();//首先初始化数据
//不断循环操作执行过程
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码值为27
if (input == 0x1b) {
system("pause");//强制等待命令暂停程序
}
}
}
更高维的,如三维、四维数组和二维数组类似考虑和想象。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。