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

目录
相关文章
|
SQL 存储 Java
Hive 特殊的数据类型 Array、Map、Struct
在Hive中,`Array`、`Map`和`Struct`是三种特殊的数据类型。`Array`用于存储相同类型的列表,如`select array(1, &quot;1&quot;, 2, 3, 4, 5)`会产生一个整数数组。`Map`是键值对集合,键值类型需一致,如`select map(1, 2, 3, &quot;4&quot;)`会产生一个整数到整数的映射。`Struct`表示结构体,有固定数量和类型的字段,如`select struct(1, 2, 3, 4)`创建一个无名结构体。这些类型支持嵌套使用,允许更复杂的结构数据存储。例如,可以创建一个包含用户结构体的数组来存储多用户信息
3890 0
|
存储 数据处理 C语言
Python二进制通信:struct、array、ctypes模块比较
Python是一种广泛应用于数据处理和网络编程的语言。在与C语言或其他设备进行二进制通信时,Python需要使用一些专门的模块来转换数据格式。本文将介绍三个常用的模块:struct、array、ctypes,并从结构说明和性能分析两方面进行比较。
523 0
Python二进制通信:struct、array、ctypes模块比较
|
C语言 C++ 编译器
flexible array柔性数组、不定长的数据结构Struct详解
柔性数组,这个名词对我来说算是比较新颖的,在学习跳跃表的实现时看到的。这么好听的名字,的背后到底是如何的优雅。 柔性数组,其名称的独特和迷惑之处在于“柔性”这个词。在C/C++中定义数组,是一个定长的数据结构,最常用的定义如下 int arr[100]; 上述代码的中arr数组的长度已知,我们把上面的语句称之为声明语句,因为在编译期数组的长度已经确定了,我暂且发明了一个词来称呼这类数组——“刚性”数组(声明,这个词是我臆想的,是不存在这种说法的)。
2505 0
|
11月前
|
测试技术 PHP 开发者
PHP 数组查找:为什么 `isset()` 比 `in_array()` 快得多?
PHP 数组查找:为什么 `isset()` 比 `in_array()` 快得多?
|
人工智能 Java
Java 中数组Array和列表List的转换
本文介绍了数组与列表之间的相互转换方法,主要包括三部分:1)使用`Collections.addAll()`方法将数组转为列表,适用于引用类型,效率较高;2)通过`new ArrayList&lt;&gt;()`构造器结合`Arrays.asList()`实现类似功能;3)利用JDK8的`Stream`流式计算,支持基本数据类型数组的转换。此外,还详细讲解了列表转数组的方法,如借助`Stream`实现不同类型数组间的转换,并附带代码示例与执行结果,帮助读者深入理解两种数据结构的互转技巧。
1028 1
Java 中数组Array和列表List的转换
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
375 67
|
JavaScript 前端开发 API
JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、复杂API请求、DOM操作、搜索和过滤等,array.map()的使用详解(附实际应用代码)
array.map()可以用来数据转换、创建派生数组、应用函数、链式调用、异步数据流处理、复杂API请求梳理、提供DOM操作、用来搜索和过滤等,比for好用太多了,主要是写法简单,并且非常直观,并且能提升代码的可读性,也就提升了Long Term代码的可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
移动开发 运维 供应链
通过array.some()实现权限检查、表单验证、库存管理、内容审查和数据处理;js数组元素检查的方法,some()的使用详解,array.some与array.every的区别(附实际应用代码)
array.some()可以用来权限检查、表单验证、库存管理、内容审查和数据处理等数据校验工作,核心在于利用其短路机制,速度更快,节约性能。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
供应链 JavaScript 前端开发
通过array.every()实现数据验证、权限检查和一致性检查;js数组元素检查的方法,every()的使用详解,array.some与array.every的区别(附实际应用代码)
array.every()可以用来数据验证、权限检查、一致性检查等数据校验工作,核心在于利用其短路机制,速度更快,节约性能。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
Web App开发 存储 前端开发
别再用双层遍历循环来做新旧数组对比,寻找新增元素了!使用array.includes和Set来提升代码可读性
这类问题的重点在于能不能突破基础思路,突破基础思路是从程序员入门变成中级甚至高级的第一步,如果所有需求都通过最基础的业务逻辑来做,是得不到成长的。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~