背景:我记不住各种语言的语法,例如C、Java、Go、Python、JavaScript,大概就是常用的这几种语言,每种语言有其自己的语法规范,有的时候会记混了,所以想记录一下细节。这个系列会不定期的更新,本期是各类语言的数组。
一、C语言的数组
C语言的数组是非常重要的一种数据结构,下面是关于C语言数组的各种使用语法。
如果引用不存在的数组成员(即越界访问数组),并不会报错,所以必须非常小心。
/*
* C 语言数组
*/
int scores[100];
// 越界访问,不会报错,需要注意
scores[100] = 51;
// 可以用大括号赋值。
int a[5] = {22, 37, 3490, 18, 95};
// b[3]、b[4] 默认初始化为0
int b[5] = {32,58,123};
// 同时可以为指定位置的变量设置值,其余初始化为0
int c[15] = {[2] = 29, [9] = 7, [14] = 48};
// 省略数组的大小,由初始化的值自动计算大小,这时d的大小是3
int d[] = {22, 37, 3490};
// 数组的所占据的字节数,得出12个字节
int e[] = {22, 37, 3490};
int arrLen = sizeof(e); // 12
// 数组中的成员数目
sizeof(e)/sizeof(e[0])
// 多维数组
int board[10][10];
// 初始化
int a[2][5] = {
{0, 1, 2, 3, 4},
{5, 6, 7, 8, 9}
};
// 指定位置进行初始化
int a[2][2] = {[0][0] = 1, [1][1] = 2};
// 数组均是线性存储,也可以用一个大括号进行赋值操作
int a[2][2] = {1, 0, 0, 2};
// 变长数组,也就是数组大小不定,是一个变量,无法在编译期间确定,只在运行时候确定。
int i = 10;
int a1[i];
int a2[i + 5];
int m = 4;
int n = 5;
int c[m][n];
// 数组指针
// 数组名是一个指针,指向第一个元素的地址
int a[5] = {11, 22, 33, 44, 55};
int* p = &a[0];
// 等同于
int* p = a;
// a 与 &a[0] 是等价的, a是&a[0]的一种简写
// 可以将一个数组指针作为函数参数,传入到函数内部,在函数内部可以通过数组指针进行操作整个数组
// 这两种写法是一样的
// 写法一
int sum(int arr[], int len);
// 写法二
int sum(int* arr, int len);
// 二维数组 相当于 指针的指针
int a[4][2];
// 取出 a[0][0] 的值
*(a[0]);
// 等同于
**a
// 由于a[0]本身是一个指针,指向第二维数组的第一个成员a[0][0]。所以,*(a[0])取出的是a[0][0]的值。至于**a,就是对a进行两次*运算,第一次取出的是a[0],第二次取出的是a[0][0]。
// 通过指针的移动遍历数组,a + i的每轮循环每次都会指向下一个成员的地址,
// *(a + i)取出该地址的值,等同于a[i]
// 对于数组的第一个成员, *(a + 0)(即*a)等同于a[0]
// 公式是 a[b] == *(a + b)
// 如果指针变量p指向数组的一个成员,那么p++就相当于指向下一个成员,这种方法常用来遍历数组
// 但容易出现数组越界的问题
int a[] = {11, 22, 33, 44, 55, 999};
int* p = a;
while (*p != 999) {
printf("%d\n", *p);
p++;
}
// 遍历数组一般都是通过数组长度的比较来实现,但也可以通过数组起始地址和结束地址的比较来实现
int sum(int* start, int* end) {
int total = 0;
while (start < end) {
total += *start;
start++;
}
return total;
}
int arr[5] = {20, 10, 5, 39, 4};
printf("%i\n", sum(arr, arr + 5));
// 二维数组指针的加减
int arr[4][2];
// 指针指向 arr[1]
arr + 1;
// 指针指向 arr[0][1]
arr[0] + 1
// 数组的复制
// 第一种方式是 循环进行赋值
// 第二种方式是 使用memcpy()函数
void *memcpy(void *dest, const void *src, size_t n)
// 将src的n个字节复制到dest中
char str1[] = "Geeks";
char str2[] = "Quiz";
puts("str1 before memcpy ");
puts(str1);
/* Copies contents of str2 to str1 */
memcpy (str1, str2, sizeof(str2));
puts("\nstr1 after memcpy ");
puts(str1);
// 数组作为函数的参数传入函数内部
int sum_array(int a[], int n) {
// ...
}
int a[] = {3, 5, 7, 3};
int sum = sum_array(a, 4);
// 变长数组作为函数的参数,先写元素个数,再写元素地址
// 变量n作为参数时,顺序一定要在变长数组前面,这样运行时才能确定数组a[n]的长度,否则就会报错
int sum_array(int n, int a[n]) {
// ...
}
int a[] = {3, 5, 7, 3};
int sum = sum_array(4, a);
//函数原型可以省略参数名,所以变长数组的原型中,可以使用*代替变量名,也可以省略变量名。
int sum_array(int, int [*]);
int sum_array(int, int []);
// 变长数组的写法
int sum_array(int n, int m, int a[n][m]);
// 还可以使用 数组字面量作为参数
int sum = sum_array((int []){2, 3, 4, 5}, 4);
// 字符串数组
// 在C语言中,字符串是一个字符型数组,而字符串数组是一个二维的字符型数组,每一个字符串由\0结尾
// 语法为:
// char var_name[r][c] = {list of string};
// var_name是一个变量名
// r是字符串的个数的最大值, c是每个字符串存储的最大的字符数
char arr[3][10] = {"Geek","Geeks", "Geekfor"};
3代表3个字符串,10代表每个字符串的最大字符数。
关于字符串数组,
char
arr[3][10] = {
"Geek","Geeks"
,
"Geekfor"
};
会导致浪费内存空间,并且无法修改,
arr[0] = "china" // 这样会报错,因为arr[0]指向的是一个地址,
但是可以通过
strcpy(arr[0],"china") // 这样将会把字符串"china"拷贝到arr[0]上
为了节省空间,我们可以使用指针数组,是一个一维的数组,里面存储的是指针。
优点: 1. 可以任意修改各个字符串 2. 还不会浪费内存空间
char* arr[] = {"Geek","Geeks","Geekfor"};
arr[1] = "china"; // 这样可以修改
二、Java语言的数组
Java语言的数组和C语言数组语法不一样,但都是指针,只不过在Java,都是通过指向内存地址来进行访问,这里记录一下。
// 数组类型 : “类型[]” 这是一种类型
// 数组在初始化的时候必须使用 new 类型[n]来进行初始化
// Java的数组有几个特点:
// 第一: 数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false;
// 第二: 数组一旦创建后,大小就不可改变。
// 第三: 要访问数组中的某一个元素,需要使用索引。数组索引从0开始,
// 第四: 数组如果索引超出范围会报错,而C语言不会报错,这是Java和C语言的不同
int[] ns = new int[5];
// 数组长度通过数组的length属性来获取
ns.length
// 定义数组时直接指定初始化的元素,这样就不必写出数组大小,而是由编译器自动推算数组大小,和C语言是一致的。
int[] ns = new int[] { 68, 79, 91, 85, 62 };
或者 简写为
int[] ns = { 68, 79, 91, 85, 62 }; // 与C语言相似,只是数组类型的写法不一样。
// Java数组也是引用类型,可以理解为和C语言是一致的
// 字符串数组
String[] names = {"ABC", "XYZ", "zoo"};
String[] s = names;
names[1] = "cat";
System.out.println(names[1]); // cat
System.out.println(s[1]); // cat
// "ABC"、"XYZ"和"zoo"是三个字符串变量,存在于内存中的不同的地址
// names[0]是一个字符串指针,指向了内存中”ABC“的地址
// names[1]是一个字符串指针,指向了内存中"XYZ"的地址
// names[2]是一个字符串指针,指向了内存中"zoo"的地址
// names也是一个指针指向初始的地址,即指向 names[0]
// 所以在names数组中 是存放各个字符串的指针,与C语言的指针数组是一样的
// 指针数组: 存放若干指针的数组。
// 当修改某个字符串时候,其实是改变了指针的指向。
names[1] = "cat"; // 首先在内存中新建一个"cat",然后将names[1]指向"cat"的地址
三、Go、Python、JavaScript
关于Go/Python/JavaScript的相关知识,后续补充,敬请期待