前端学习C语言 - 数组和字节序
数组
本篇主要介绍:一维二维数组
、字符数组
、数组名和初始化注意点
以及字节序
。
一维数组
初始化
有以下几种方式对数组初始化:
// 定义一个有5个元素的数组,未初始化
int a[5];
// 定义一个有5个元素的数组,将第一个初始化0,后面几个元素默认初始化为0
int a[5] = {0};
// 定义一个有5个元素的数组,5个元素都初始化为:2,3,4,5,6
int a[5] = {2, 3, 4, 5, 6};
// 【推荐】
// 和上一种在功能上是相同的。编译器会根据初始化列表中的元素个数(5个)自动确定数组a的大小为5
int a[] = {2,3,4,5,6};
Tip:以上写法创建的数组都是不可变大小的
。
练习1
题目
:int a[5] = {1}
,请问 a 的每个值是多少?
#include <stdio.h>
int main() {
// 将第一个初始化1,后面几个元素默认初始化为0
int a[5] = {1};
int i;
for (i = 0; i < 5; i++) {
printf("%d ", a[i]);
}
return 0;
}
输出:1 0 0 0 0
。
在C和C++中,当我们创建数组时,如果没有为数组的所有元素提供初始值,那么剩下未被初始化指定初始值的元素会被默认初始化。对于基本数据类型(如int、float、double等),默认情况下,未初始化的元素将被设置为0
练习2
题目
:如果不对 a[5]
进行初始化,将输出什么?
#include <stdio.h>
int main() {
- int a[5] = {1};
+ int a[5];
int i;
for (i = 0; i < 5; i++) {
printf("%d\n", a[i]);
}
return 0;
}
输出随机数:
开始运行...
4198784
0
4198464
0
-2014700240
运行结束。
练习3
题目
:如果将int a[5];
提到全局作用于中,输出什么?
#include <stdio.h>
int a[5];
int main() {
int i;
for(i = 0; i < 5; i++){
printf("%d ", a[i]);
}
return 0;
}
输出: 0 0 0 0 0
练习4
题目
:这段代码有什么错误?
#include <stdio.h>
int main() {
int i = 10;
int a[i] = {0};
return 0;
}
运行:
开始运行...
# 不允许初始化可变大小的对象。即 i 是可变的。
/workspace/CProject-test/main.c:5:11: error: variable-sized object may not be initialized
int a[i] = {0};
^
1 error generated.
运行结束。
结论
:数组长度不能是变量。
如果换成 #define 常量
还有问题吗?
#include <stdio.h>
#define i 10
int main() {
// int i = 10;
int a[i] = {0};
return 0;
}
如果换成 #define 常量
就正常,前面我们知道 #define 是文本替换
。
数组名
题目
:定义一个数组a,请问 a
、&a[0]
、&a
的含义是什么?
#include <stdio.h>
int main() {
int a[5] ={1};
printf("%p\n", a);
printf("%p\n", &a[0]);
printf("%p\n", &a);
printf("-----\n");
printf("%p\n", a + 1);
printf("%p\n", &a[0] + 1);
printf("%p\n", &a + 1);
return 0;
}
运行:
开始运行...
0x7ffdfb131f00
0x7ffdfb131f00
0x7ffdfb131f00
-----
0x7ffdfb131f04
0x7ffdfb131f04
0x7ffdfb131f14
运行结束。
Tip: printf 中的 %p 打印的就是内存地址
。内存地址通常以十六进制
形式表示。
上半部分都是输出的都是 0x7ffdfb131f00
。
但下半部分加1后,结果明显不同。其中:
0x7ffdfb131f04 - 0x7ffdfb131f00 = 0x4
,转为十进制是4,一个 int 就是4个字节0x7ffdfb131f14 - 0x7ffdfb131f00 = 0x14
,转为十进制数是20,刚好是数组 a 的字节数(5*4)
结论:
a
- 数组名。表示首元素的地址,加 1 是加一个元素(比如这里4个字节)&a[0]
- 表示首元素地址,加 1 是加一个元素(比如这里4个字节)&a
- 表示整个数组。加1相当于跨越了整个数组
冒泡排序
之前我们写过冒泡排序的例子,我们将该示例用 C 语言重写如下(函数部分后文会讲):
#include <stdio.h>
void bubbleSort(int arr[], int n) {
// 比较轮数,每轮都会将一个值冒泡到正确的位置
for (int i = 0; i < n; i++) { // 第i轮冒泡
for (int j = 0; j < n - i - 1; j++) { // 第i轮冒泡需要比较n-i-1次
// 出界则为 false,不会交换
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main() {
int arr[] = {4, 3, 2, 1};
// 计算数组长度。sizeof(arr) - 返回数据类型或变量所占内存大小(字节);arr[0] - 一个元素的字节数。
int length = sizeof(arr) / sizeof(arr[0]);
bubbleSort(arr, length);
// 输出
for (int i = 0; i < length; i++) {
printf("%d ", arr[i]);
}
return 0;
}
// Output: 1 2 3 4
字节序
字节序(Byte Order)是指在存储和表示多字节数据时,字节的顺序
排列方式。
思考这样一个问题
int a[5]
有5个元素,每个元素4个字节,在内存中是一块连续的空间。表示如下:
索引 | a[0] | a[1] | a[2] | a[3] | a[4] |
---|---|---|---|---|---|
地址 | 0x100 | 0x104 | 0x108 | 0x10C | 0x110 |
我们可以将a[0]
称作低地址
,a[4]
称作高地址
。a数组中每个元素的四个字节,最左侧字节称作低地址
,最右侧字节称作高地址
。就像这样:
低地址 | 高地址 | ||
---|---|---|---|
________ | ________ | ________ | ________ |
数组 a 中每个元素中是一个整数,比如 a[0] = 1
,在内存中是4个字节,共32位,其二进制表示为:00000000 00000000 00000000 00000001
。最左侧是高字节
,最右侧是低字节
,就像这样:
高字节 | 低字节 | ||
---|---|---|---|
00000000 | 00000000 | 00000000 | 00000001 |
请问 1 的高字节(00000000)放在低地址还是高地址?
大端序和小端序
不同的计算机架构和处理器采用不同的字节序(Byte Order)。常见的字节序有两种:
大端序
(Big Endian),低字节对应高地址,高字节对应低地址。1 对应00000000 00000000 00000000 00000001
小端序
(Little Endian),低字节对应低地址,高字节对应高地址。1 对应00000001 00000000 00000000 00000000
低地址 | 高地址 | |||
---|---|---|---|---|
大端序 | 00000000 | 00000000 | 00000000 | 00000001 |
小端序 | 00000001 | 00000000 | 00000000 | 00000000 |
Tip: 不同字节序的选择涉及到如何组织和解释二进制数据。字节序的重要性体现在跨平台数据交互和网络通信上。如果两个设备使用不同的字节序,就需要进行适当的数据转换才能正确解读和处理数据
二维数组
可以理解成一维数组中每个元素又是一个一维数组。例如 a[3][4]
就像这样:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 行 | ||||
1 行 | ||||
2 行 |
a[0]
、a[1]
、a[2]
,每一行就是一个一维数组。
初始化
有多种方式进行二维数组的初始化,效果也不尽相同。请看示例:
int a[3][4];
未初始化,数组 a 中都是随机值。请看示例:
#include <stdio.h>
int main() {
int a[3][4];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", a[i][j]);
}
printf("\n");
}
return 0;
}
输出:
开始运行...
-1833069321 32764 4198917 0
0 0 0 0
4198848 0 4198464 0
运行结束。
- 部分初始化。示例如下:
// 输出:1 2 3 4 5 6 7 8 9 10 11 12
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
// 输出:1 0 0 0 5 6 0 0 0 0 0 0
int a[3][4] = {{1}, {5, 6}};
- 全部初始化。示例如下:
// 输出:1 2 3 4 5 6 7 8 9 10 11 12
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
// 0 0 0 0 0 0 0 0 0 0 0 0
// 此种写法不能保证所有编译器
int a[3][4] = {}
- 行数可以省略。以下两行代码等效:
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
// 省略行。类似一维数组中省略元素个数。
int a[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
数组名
题目
:定义一个二维数组a,请问 &a[0][0]
、a
、&a[0]
、&a
的含义是什么?
#include <stdio.h>
int main() {
int a[3][4];
printf("%p\n", &a[0][0]);
printf("%p\n", a);
printf("%p\n", a[0]);
printf("%p\n", &a);
printf("-----\n");
printf("%p\n", &a[0][0] + 1);
printf("%p\n", a + 1);
printf("%p\n", a[0] + 1);
printf("%p\n", &a + 1);
return 0;
}
输出:
开始运行...
0x7fffadc7d310
0x7fffadc7d310
0x7fffadc7d310
0x7fffadc7d310
-----
0x7fffadc7d314
0x7fffadc7d320
0x7fffadc7d314
0x7fffadc7d340
运行结束。
上半部分都是输出的都是 0x7fffadc7d310
。
下半部分每个加1,差异就显现出来。
0x7fffadc7d314 - 0x7fffadc7d310 = 0x4,转为十进制是4个字节
&a[0][0] + 1
0x7fffadc7d320 - 0x7fffadc7d310 = 0x10,转为十进制是16个字节,每个元素是4个字节,也就是4(16/4)个元素,表示一行
a + 1
0x7fffadc7d314 - 0x7fffadc7d310 = 0x4,转为十进制是4个字节
a[0] + 1
0x7fffadc7d340 - 0x7fffadc7d310 = 0x30,转为十进制是48个字节,每个元素是4个字节,也就是 12(48/4)个元素,表示整个数组
&a + 1
结论:
&a[0][0]
- 首行首元素地址,加 1 是加一个元素(比如这里4个字节)a
- 地址名,表示首行地址,加 1 就是加一行a[0]
- 首行首元素地址,加 1 是加一个元素(比如这里4个字节)&a
- 表示整个数组。加1相当于跨越了一个数组
练习
题目
:数组 a[3][4]
,哪个不能表示a[1][1]
的地址?
A、a[1] + 1
B、&a[1][1]
C、(*(a + 1)) + 1
D、a + 5
答案:D。
分析:根据上文学习,我们知道 A和B能表示,其中D是加5行,肯定错。C由于没学指针,暂时不管。
字符数组
在 C 语言中,字符串可以用字符数组来表示,即用一个数组来保存一串字符,每个字符用一个字节来存储,末尾有一个特殊的空字符 '\0' 来表示字符串的结束。
#include <stdio.h>
int main() {
char str[] = {'h', 'e', 'l', 'l', 'o', '\0'};
printf("%s", str);
return 0;
}
在字符数组的初始化末尾一定要添加空字符 '\0'(笔者使用的在线编辑器没报错),否则在使用字符串函数处理字符串时,可能会出现意外的错误。也可以将上面的代码简化为以下形式:
char str[] = "hello";
这样就可以不用手动添加空字符了,编译器会自动为字符串添加结尾的空字符。
练习
题目
:在输入的字符串中,在指定位置插入指定字符
实现:
#include <stdio.h>
// string.h 是 C 语言中的头文件,用于提供一些字符串处理操作的函数和宏定义
#include <string.h>
void insertChar(char str[], int pos, char ch) {
// 获取字符串的长度
int len = strlen(str);
// 检查插入位置是否有效
if (pos < 0 || pos > len)
return;
// 将指定位置后的字符往后移动一位
for (int i = len; i >= pos; i--) {
str[i + 1] = str[i];
}
// 在指定位置插入字符
str[pos] = ch;
}
int main() {
char str[100];
int pos;
char ch;
printf("请输入字符串。例如 hello world:\n");
scanf("%[^\n]", str);
printf("请输入要插入的位置:");
scanf("%d", &pos);
printf("请输入要插入的字符:");
scanf(" %c", &ch);
insertChar(str, pos, ch);
printf("\n修改后的字符串:%s\n", str);
return 0;
}
运行:
开始运行...
请输入字符串。例如 hello world:
a-b-c d e-f-g
请输入要插入的位置:4
请输入要插入的字符:x
修改后的字符串:a-b-xc d e-f-g
运行结束。
前端学习C语言 - 数组和字节序的更多相关文章
- iOS学习04C语言数组
1.一维数组 数组:具有相同类型的成员组成的一组数据 1> 定义 元素:数组中存放的数据成为数组的元素 数组是构造类型,用{...}来给构造类型赋初始值,类型修饰符用来表示元素的类型 类 ...
- c语言中网络字节序和主机字节序的转换
函数说明 相关函数:htonl, htons, ntohl 头文件:#include <netinet/in.h> 定义函数:unsigned short int ntohs(unsi ...
- c++和python如何实现主机字节序和网络字节序的相互转换
在上一篇文章网络编程:主机字节序和网络字节序中,介绍了主机字节序和网络字节序的基本概念以及在实际的编程中,何时需要进行网络字节序和主机字节序的转换.本篇文章着重介绍使用c++和python语言,如何实 ...
- C语言字节对齐问题详解(对齐、字节序、网络序等)
首先说明一下,本文是转载自: http://www.cnblogs.com/clover-toeic/p/3853132.html 博客园用的少,不知道怎么发布转载文章,只能暂时这样了. 引言 考虑下 ...
- C语言中的位域、字节序、比特序、大小端
转:http://www.360doc.com/content/13/0624/10/496343_295125641.shtml 1.比特序 / 位序 / bit numbering / bit ...
- 前端学习 第三弹: JavaScript语言的特性与发展
前端学习 第三弹: JavaScript语言的特性与发展 javascript的缺点 1.没有命名空间,没有多文件的规范,同名函数相互覆盖 导致js的模块化很差 2.标准库很小 3.null和unde ...
- C/C++学习笔记---高地址、低地址、大段字节序、小段字节序
字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端.大端两种字节顺序. 小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处: 大端字节序是高字节数据存放在低地址 ...
- 【转】go语言的字节序
原文:http://lihaoquan.me/2016/11/5/golang-byteorder.html 这个人的博客写的不错,品质也比较高. 我应该也要有这种精神,这种态度.深入到计算机的世界中 ...
- 前端学习之——js解析json数组
** 前端学习之——js解析json数组** 解析json数组即对JSONArray的遍历 一.对于标准的json数组如: var result=[{"flag":1," ...
- 09.C语言:预处理(宏定义)、字节序、地址对齐
一.预处理 预处理 gcc -E Hello.c -o hello.i 编译 gcc -S hello.i -o hello.s 汇编 gcc -c hello.s -o hello.o 链接 gcc ...
随机推荐
- 机器学习基础09DAY
分类算法之逻辑回归 逻辑回归(Logistic Regression),简称LR.它的特点是能够是我们的特征输入集合转化为0和1这两类的概率.一般来说,回归不用在分类问题上,因为回归是连续型模型,而且 ...
- KMP算法的研究
前脚学后脚忘,是时候给自己通俗易懂的总结一下了 KMP是什么 在计算机科学中,Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个字符串S内查找一个词W的出现位置.一个词在不 ...
- 运输问题—R实现
table { margin: auto } 运输问题 随着社会和经济的不断进步,现代物流业蓬勃发展,如何充分利用时间.信息.仓储.配送和联运体系创造更多的价值,是物流运作必须解决的问题.运输问题(t ...
- 二进制安装 Kubernetes(k8s)
二进制安装 Kubernetes(k8s) Kubernetes 开源不易,帮忙点个star,谢谢了 介绍 kubernetes(k8s) 二进制安装 后续尽可能第一时间更新新版本文档 1.23.3 ...
- [Java] 多线程系列之Fork/Join框架[转载]
1 工作原理 1.1 核心思想:分而治之 & 并行执行 Fork/Join框架是Java 7提供的一个用于并行执行任务的框架, 核心思想就是把大任务分割成若干个小任务,最终汇总每个小任务结果后 ...
- Design as You See FIT 阅读笔记
Design as You See FIT 作者及会议名称:DATE 2009, Daniel Holcomb, UC Berkeley 本文的重点贡献:提出了一种新方法计算时序电路发生系统级故障对输 ...
- 如何优雅的使用ipv6穿透内网
背景 随着ipv6的普及,家庭宽带已经全面支持ipv6,通过简单的设置就可以让自己的内网设备获取到ipv6地址.不过这里的ipv6地址也不是固定,会定期的变化,不过通过DDNS可以解决这个问题.但是这 ...
- ORA-01093: ALTER DATABASE CLOSE only permitted with no sessions connected DG开启MRP失败
问题描述:在10.2.0.5的备库中open状态下开启实时同步,开启失败.一直卡着,只能强制停止 SQL> alter database recover managed standby dat ...
- odoo 开发入门教程系列-继承(Inheritance)
继承(Inheritance) Odoo的一个强大方面是它的模块化.模块专用于业务需求,但模块也可以相互交互.这对于扩展现有模块的功能非常有用.例如,在我们的房地产场景中,我们希望在常规用户视图中直接 ...
- MQ高级
1.消息可靠性 消息从发送,到消费者接收,会经理多个过程: 其中的每一步都可能导致消息丢失,常见的丢失原因包括: 发送时丢失: 生产者发送的消息未送达exchange 消息到达exchange后未到达 ...