struct vs array

简介:
C里面array是一个比较特殊的类型, 例如char a[10]= "abcdef"; 在编译时, a会替换成存储实际内容的内存首地址. 所以没有地方存储变量a.
因此使用a和&a 都可以打印a的地址.
如 : 
[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>

char a[10] = "abc";

int main() {
  fprintf(stdout,"addr a:%p, addr &a:%p\n", a, &a);
  return 0;
}
结果 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
addr a:0x6008c4, addr &a:0x6008c4


struct 定义的变量, 变量就是变量, 它不是一个指针. 这点和array不同. 例如 : 
[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>

struct fish {
  int a;
  float c;
  char b[10];
};

int main() {
  struct fish f1 = {1, 9.9, "abcd"};
  fprintf(stdout,"addr f1:%p, addr &f1:%p\n", f1, &f1);
  return 0;
}
报错, 也就是说f1 是个变量, 要取地址需要使用&, 这点和其他的变量相同 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
cc1: warnings being treated as errors
./d.c: In function ‘main’:
./d.c:11: warning: format ‘%p’ expects type ‘void *’, but argument 3 has type ‘struct fish’

修改后 :
[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>

struct fish {
  int a;
  float c;
  char b[10];
};

int i;

int main() {
  struct fish f1 = {1, 9.9, "abcd"};
  i = 10;
  fprintf(stdout,"addr &i:%p, addr &f1:%p\n", &i, &f1);
  return 0;
}
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
addr &i:0x600934, addr &f1:0x7fffdd6bec90


虽然使用%p 打印的 a或者&a 内容是一致的, 但是a 和 &a 是有区别的. 可以通过测试来看一看.
a[1] 等同于 *(a+1) , 这里 a+1 用到了指针的地址运算, 即a的地址加上1个数组中的元素占用的空间. 如int a[], 一个元素占用4字节. char a[]中一个元素占用1字节. 如下 : 

[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>

char a[10] = "abc";
int b[10] = {1,2,3,4,5};

int main() {
  fprintf(stdout,"addr a:%p, addr a[1]:%p, addr a+1:%p\n", a, &(a[1]), &(*(a+1)));  // &(*(a+1)) 替换成 (a+1) 是一样的
  fprintf(stdout,"addr b:%p, addr b[1]:%p, addr b+1:%p\n", b, &(b[1]), &(*(b+1)));  // &(*(b+1)) 替换成 (b+1) 是一样的
  return 0;
}
结果 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
addr a:0x600940, addr a[1]:0x600941, addr a+1:0x600941
addr b:0x600960, addr b[1]:0x600964, addr b+1:0x600964


既然 数组名 a 是指针, 那&a 是什么呢? 能不能像a这样做指针的地址运算呢?
可以, 但是已经和a+1 得到的结果不一样了, 那么(&a)+1 会是什么意思? 结果是什么呢?
&a 表示存储a这个数组的首内存地址, 所以(&a) +1 则表示下一个和a数组占用一样大小空间的数组的内存首地址.
换句话说a表示的是char类型的指针 , 而&a 表示的是char a[10]这个数组类型的指针 . 
因为C是强类型语言, 所以加减结果必然与类型有关. 
来看个例子 : 

[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>

char a[10] = "abc";
int b[10] = {1,2,3,4,5};

int main() {
  fprintf(stdout,"sizeof(a):%lu, sizeof(b):%lu\n", sizeof(a), sizeof(b));
  fprintf(stdout,"addr a:%p, addr a[1]:%p, addr (&a)+1:%p\n", a, &(a[1]), ((&a)+1));
  fprintf(stdout,"addr b:%p, addr b[1]:%p, addr (&b)+1:%p\n", b, &(b[1]), ((&b)+1));
  return 0;
}
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
sizeof(a):10, sizeof(b):40
addr a:0x6009a0, addr a[1]:0x6009a1, addr (&a)+1:0x6009aa  // 结果表明 0x6009aa - 0x6009a0 刚好等于10字节, a[10]的大小.
addr b:0x6009c0, addr b[1]:0x6009c4, addr (&b)+1:0x6009e8  // 结果表明 0x6009e8 - 0x6009c0 刚好等于40字节, b[10]的大小.


前面我们看到array可以用[0],[1]来取数组中的元素. 那么struct用什么来取呢? 用点. 不能用[]这种方式.
来看个例子 : 

[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
a[0]:a, a[1]:b
b[0]:1, b[1]:2
f1.age:20, f1.weight:60.500000, f1.name:linux
[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>

char a[10] = "abc";
int b[10] = {1,2,3,4,5};
struct fish {
  int age;
  float weight;
  char name[10];
};

int main() {
  struct fish f1 = {20, 60.5, "linux"};
  fprintf(stdout,"a[0]:%c, a[1]:%c\n", a[0], *(a+1));
  fprintf(stdout,"b[0]:%i, b[1]:%i\n", b[0], *(b+1));
  fprintf(stdout,"f1.age:%i, f1.weight:%f, f1.name:%s\n", f1.age, f1.weight, f1.name);
  return 0;
}
结果 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
a[0]:a, a[1]:b
b[0]:1, b[1]:2
f1.age:20, f1.weight:60.500000, f1.name:linux

但是需要注意的是, struct中, 使用(.点)来引用元素的时候和array使用[]来应用元素的区别.
数组中 , a[1] = *(a+1) 是指的内容(char). 而不是指针(pointer).
struct 中 f1.? 真实反映这个元素的内容 (int, char, 或char [])  例如 : 

[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>

struct fish {
  int age;
  float weight;
  char name[10];
};

int main() {
  struct fish f1 = {20, 60.5, "linux"};
  fprintf(stdout,"f1.age:%i, f1.weight:%f, f1.name:%s ,f1.name[1]:%c, (f1.name)[1]:%c, *((f1.name)+1):%c\n", f1.age, f1.weight, f1.name, f1.name[1], (f1.name)[1], *((f1.name)+1));
  return 0;
}
结果 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
f1.age:20, f1.weight:60.500000, f1.name:linux ,f1.name[1]:i, (f1.name)[1]:i, *((f1.name)+1):i

所以 f1.name[1],  (f1.name)[1],  *((f1.name)+1) 这三种写法是一样的.

在初始化完变量后,  struct 和 array 一样, 里面的元素都是连续存储的 . 
例如 : 

[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>

char a[] = "abcdef";
struct fish {
  int age;
  float weight;
  char name[10];
};

int main() {
  struct fish f1 = {20, 60.5, "linux"};
  fprintf(stdout,"sizeof(char):%lu, sizeof(int):%lu, sizeof(float):%lu, sizeof(char [10]):%lu\n", sizeof(char), sizeof(int), sizeof(float), sizeof(char [10]));
  fprintf(stdout,"sizeof(f1.age):%lu, sizeof(f1.weight):%lu, sizeof(f1.name):%lu\n", sizeof(f1.age), sizeof(f1.weight), sizeof(f1.name));
  fprintf(stdout,"sizeof(a):%lu, addr a:%p, addr a[0]:%p, addr a[1]:%p, addr a[2]:%p\n", sizeof(a), a, a, a+1, a+2);
  fprintf(stdout,"sizeof(f1):%lu, addr &f:%p, addr f1.age:%p, addr f1.weight:%p, addr f1.name:%p\n", sizeof(f1), &f1, &(f1.age), &(f1.weight), &(f1.name));
  return 0;
}

结果 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
sizeof(char):1, sizeof(int):4, sizeof(float):4, sizeof(char [10]):10
sizeof(f1.age):4, sizeof(f1.weight):4, sizeof(f1.name):10
sizeof(a):7, addr a:0x600aac, addr a[0]:0x600aac, addr a[1]:0x600aad, addr a[2]:0x600aae
sizeof(f1):20, addr &f:0x7fff0c25ec30, addr f1.age:0x7fff0c25ec30, addr f1.weight:0x7fff0c25ec34, addr f1.name:0x7fff0c25ec38


在赋值方面, array 和 struct 也有所不同. 例如
[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>
#include <string.h>

char a[] = "abcdef";
struct fish {
  int age;
  float weight;
  char name[10];
  char * nick;
};

int main() {
  struct fish f1 = {20, 60.5, "linux", "unix"};
  // 结构可以通过拷贝来赋值 , 注意每个元素都会拷贝一份, 对于指针类型的元素,  拷贝的则是指针(所以f2 和f1 的nick 将指向同一个区域.)
  struct fish f2 = f1;
  char b[10];
  *(b) = a[0];
  *(b+1) = a[1];
  *(b+2) = *(a+2);
  *(b+3) = a[3];
  b[4] = a[4];
  b[5] = a[5];
  // 数组不能简单的用等于来直接拷贝. 以下三种赋值都是错误的.
  // 再一次说明了array b这个b已经在编译时被替换为地址, 而struct fish f2它是个变量名, 才能够通过此方式赋值.
  // b = a;
  // *b = *a;
  // *b = a;
  fprintf(stdout, "a:%s, b:%s\n", a, b);
  fprintf(stdout, "addr f1:%p, addr f2:%p\n", &f1, &f2);
  fprintf(stdout, "&(f1.age):%p, &(f1.weight):%p, &(f1.name):%p, &(f1.nick):%p, f1.name:%p, f1.nick:%p\n", &(f1.age), &(f1.weight), &(f1.name), &(f1.nick), f1.name, f1.nick);
  fprintf(stdout, "&(f2.age):%p, &(f2.weight):%p, &(f2.name):%p, &(f2.nick):%p, f2.name:%p, f2.nick:%p\n", &(f2.age), &(f2.weight), &(f2.name), &(f2.nick), f2.name, f2.nick);
  return 0;
}

结果 : 
f1.nick和f2.nick 打印%p 结果一致, 说明f1.nick和f2.nick指向同一个字符串.
struct vs array - 德哥@Digoal - The Heart,The World.

[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d && ./d
a:abcdef, b:abcdef
addr f1:0x7fff78ff9af0, addr f2:0x7fff78ff9ad0
&(f1.age):0x7fff78ff9af0, &(f1.weight):0x7fff78ff9af4, &(f1.name):0x7fff78ff9af8, &(f1.nick):0x7fff78ff9b08, f1.name:0x7fff78ff9af8, f1.nick:0x400748
&(f2.age):0x7fff78ff9ad0, &(f2.weight):0x7fff78ff9ad4, &(f2.name):0x7fff78ff9ad8, &(f2.nick):0x7fff78ff9ae8, f2.name:0x7fff78ff9ad8, f2.nick:0x400748

相关文章
|
存储 数据处理 C语言
Python二进制通信:struct、array、ctypes模块比较
Python是一种广泛应用于数据处理和网络编程的语言。在与C语言或其他设备进行二进制通信时,Python需要使用一些专门的模块来转换数据格式。本文将介绍三个常用的模块:struct、array、ctypes,并从结构说明和性能分析两方面进行比较。
196 0
Python二进制通信:struct、array、ctypes模块比较
|
C语言 C++ 编译器
flexible array柔性数组、不定长的数据结构Struct详解
柔性数组,这个名词对我来说算是比较新颖的,在学习跳跃表的实现时看到的。这么好听的名字,的背后到底是如何的优雅。 柔性数组,其名称的独特和迷惑之处在于“柔性”这个词。在C/C++中定义数组,是一个定长的数据结构,最常用的定义如下 int arr[100]; 上述代码的中arr数组的长度已知,我们把上面的语句称之为声明语句,因为在编译期数组的长度已经确定了,我暂且发明了一个词来称呼这类数组——“刚性”数组(声明,这个词是我臆想的,是不存在这种说法的)。
2148 0
|
2月前
|
JavaScript 前端开发
总结TypeScript 的一些知识点:TypeScript Array(数组)(下)
一个数组的元素可以是另外一个数组,这样就构成了多维数组(Multi-dimensional Array)。
|
2月前
|
存储 JavaScript 前端开发
总结TypeScript 的一些知识点:TypeScript Array(数组)(上)
数组对象是使用单独的变量名来存储一系列的值。
|
3月前
|
存储 安全 Swift
在Swift中,数组(Array)
在Swift中,数组(Array)
29 3
|
4月前
|
Ruby
|
6月前
|
存储 Java 索引
【面试题精讲】ArrayList 和 Array(数组)的区别?
【面试题精讲】ArrayList 和 Array(数组)的区别?
|
5月前
|
算法 Python
数组倍增(Array Doubling
数组倍增(Array Doubling)是一种常见的算法技术,用于解决数组相关的查找、插入、删除等问题。该技术的核心思想是将数组的大小乘以 2,新数组的长度是原数组长度的两倍,然后将原数组中的元素复制到新数组中。在某些情况下,这种技术可以提高算法的效率,尤其是对于动态数据结构的问题。
125 1
|
2月前
Google Earth Engine(GEE)——reducer中进行array数组的获取和分析
Google Earth Engine(GEE)——reducer中进行array数组的获取和分析
33 0
|
3月前
|
Rust 索引 Windows
Rust 原始类型之数组array内置方法
Rust 原始类型之数组array内置方法
51 0
Rust 原始类型之数组array内置方法