嵌入式Linux C(二)——数据类型(详)
文章目录
一、数据类型
分类:
基本数据类型:(内置:编译器自带的类型):int(4)、short(2)、long(8)、long long、char(1)、float(4)、double(8)
复合数据类型:(多个内置类型的组成的新类型):数组、union、struct、enum
void类型:void *(万能指针):多态
二、定义变量
(注意事项:编码规范)
- 变量的可读性 形容词_名词 (不允许用拼音命名!!!) int n; int sum_result;
- 循环变量:int i;int k;
三、基本数据类型需掌握的知识点
3.0 类型转化
C语言类型转换:不安全,可以将任何类型之间转换,有可能造成数据丢失(可以隐式输出,直接编译)
安全的类型转换:先检查两个类型是否可以转换
强制类型转换:(类型名称)变量名
如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如下图所示。
3.1 各种数据类型的字节长度
(变量占用内存的大小:字节)int num = 5;
注:计算机便是内存大小的单位:8bit位 = 1个字节、16bit = 2个字节 = 半字、32bit = 字、双字、kb
1. 数据类型长度
int len = sizeof(num);//sizeof计算数据类型和变量的内存大小
//int len = sizeof(int);
sizeof是一个运算符
int num;
int len = sizeof num; //√
int len = sizeof int;//×
int len = sizeof(num);//√
2. 指针长度
- 指针的长度:int *; char *
- 指针类型:保存地址,操作系统中地址的长度是固定的,是由操作系统位数决定的,64位系统是8个字节、32位系统是4个字节
3. 数组长度
int arry_int[100];//400
char arry_char[100];//100
4. 字符串长度
字符串的长度:int strlen(char *stc) //不统计’\0’
#include <stdio.h>
#include <string.h>
int main()
{
char *ptr = "hello world";
printf("sizeof(*ptr) = %ld\n",sizeof(ptr));
printf("strlen(*ptr) = %ld\n",strlen(ptr));
printf("strlen(hello world) = %ld\n",strlen("hello world"));
return 0;
}
小结:如何测量数据类型的大小
sizeof运算符
sizeof(变量名)
sizeof (数据类型)
strlen 统计字符串中字符的个数
3.2 各种数据类型的取值范围
(计算机是以补码形式保存数据,为解决+0 -0问题)
原码、反码、补码
计算机里保存:补码
正数:原码 = 补码
负数:
补码 = 原码取反 + 1
原码 = 补码取反 + 1
unsigned char:0 - 255(2^8-1)
signed char:-128-127
- 0 000 0000 = 0
- 0 111 1111 = 127
- 1 000 0000 = -128
111 1111
+1 = 2^7 - 1 + 1
1 000 0000 =-128- 1 111 1111 = -1
000 0000
+1
1 000 0001 = -1
计算机为什么提出补码存储?
解决 +0 与-0
0 000 0000 = +0
1 000 0000 = -0
printf("%d",~2)
0000 0010
1111 1101
000 0010
+1
1 000 0011 = -3
//例
char ch = -128;
ch = ch - 1; //溢出
//计算机不做减法,只做加法
//1 000 0000 //-128
//1 111 1111 //-1
//0 111 1111 = 127//上面两个相加得出结果
//例
char a[1000]
for (int i = 0; i < 1000; i++)
{
a[i] = i - 1;
//i = 127 a[127] = -128
//i = 128 a[128] = 127
//i = 128 a[129] = 126
//..
//i = 255 a[255] = 0; ‘\0’
}
printf("strlen(a) = %ld\n",strlen(a));
//结果是255 可以好好想想
//例1
char ch =127;
ch = ch + 1;
printf("%d",ch);// 溢出,输出-128
//例2
char ch =127;
printf("%d",ch + 1);//输出128
//例1,是+1后回写到内存中,造成溢出
//例2,是直接输出,所以不存在回写
3.3 无符号和有符号的移植性
signed VS unsigned
有的编译器默认是有符号,有的是无符号
如果不解决,可能导致不同编译器,编译出错
typedef
关键字:给数据类型重命名
typedef unsigned char uChar;
typedef signed char Char;
typedef unsigned int len; //命名易懂
typedef struct student Stu; //结构体重命名,提高编码效率
- 解决signed、unsigned带来的代码移植性的问题
- 提高代码的可读性
- 提高编码效率
四、变量和常量
- 全局变量和局部变量:字节长度、生命周期、存储区域
- 作用域:可见范围
局部变量:在函数体里定义的变量–所在函数(出了函数不可见)
全局变量:在函数体外定义的变量–整个全局(需要用外部声明)extern int g_count - 生命周期:所在内存空间的分配-释放的过程
局部变量:所在函数体执行时,分配空间,执行结束、释放空间
全局变量:所在程序执行时,分配空间,执行结束,释放空间 - 存储区域
CPU、硬盘、内存(物理内存,属于稀缺资源)
概念:物理内存与虚拟内存
vim hello.c
gcc hello.c -o hello
存储在硬盘上
CPU读取内存,运算完,回写内存
int num = 5;
*(&num) = 10;
printf("%p\n",&num);
//这个地址在是虚拟内存空间,以便用户不能轻易的访问到物理内存
操作系统会给用户4G的虚拟地址,通过MMU,实现虚拟地址空间(VA)与物理地址空间(PA)的映射
内存4G:1G内核+3G(栈空间、堆空间、数据段、代码段)
- 栈空间:
存放:局部变量、函数形参、自动变量(少)
特点:先进后出、系统管理(操作系统决定释放与分配) - 堆空间:
存放:malloc、ralloc、calloc分配的空间
特点:先进先出、用户管理 - 数据段
再划分:
bss:未初始化的全局变量
or:常量
静态数据区:static修饰静态变量、初始化的全局变量
初始化未定义,高版本都是0
局部变量:存储在栈空间
全局变量:存储在数据段
五、格式化输出
unsigned int usi_num = 5;
signed int si_num = -5;
int array[3] = {1,2,3};
char ch = 'a';
char *ptr = "hello world";
char src[100] = "hello";
short s_sum = 6;
long l_num = 12345678;
float f_num = 1.123;
double d_num = 1.324;
printf("usi_num = %u\n",usi_num);
printf("si_num = %d\n",si_num);
for(int i = 0; i < sizeof(array)/sizeof(int); i++)
{
printf("array[%d] = %d\n",i,array[i]);
}
printf("ch = %c\n",ch);
printf("ptr = %s\n",ptr);
while(*ptr != '\0')
{
printf("%c\n",*ptr);
ptr++;
}
//数组名保存数组首元素地址
printf("src = %s\n",src);
printf("s_sum = %d\n",s_sum);
printf("l_sum = %ld\n",l_sum);
printf("d_sum = %lf\n",d_sum);
printf注意点
printf:行缓冲:满一行(4096)或者遇到’\n’或者被强行输出时,数据才会被输出
printf语句后面要加上\n
printf("hello world!");
while(1);//不输出
printf使用技巧
printf使用技巧:C语言printf()函数中一些不为人知的技巧!
格式化输入
scanf格式化才能输入
注意点
- 不加\n
- 加&
- 指针类型不兼容(%hd、%ld)
- 跳出数组时,加上
\0
- getchar()处理垃圾
- 数据溢出,很危险
- 遇到
空格
就停止
gets();获取句子,无缓冲
附
- 计算机不做减法,只做加法
- 在c语言中,用typedef重命名后,原来的变量名还可以用
- MMU的解释:内存管理单元MMU
- 定义只能定义一次,而声明可以声明很多次。先声明,再使用
- 指针需要给其地址
疑问及其解决
疑问1::printf("%d",~2)为什么不直接是正数 1111 1101
解决1:
默认确实是有符号型,所以要看符号位
疑问点2:
unsigned char和unsigned int一样吗?为什么都是输出-3
解决2:
char是8个bit=1个字节
int是32个bit=4个字节(VC中是4个字节、ANSI标准定义中是2个字节)
关于bit等概念,数据范围看:
32位操作系统int类型最大值是多少?
疑问3
大数问题
利用数组连续性,将大数每一位上的数字单独取出放入对应的数组单元中,然后再对每一位做单独的加减乘运算,期间需处理好进位和借位以及其它一些细节性的问题。
疑问4
typedef VS 宏
typedef与#define的区别
typedef只是为了增加可读性而为标识符另起的新名称(仅仅只是个别名),而#define原本在C中是为了定义常量,到了C++,const、enum、inline的出现使它也渐渐成为了起别名的工具。有时很容易搞不清楚与typedef两者到底该用哪个好,如#define INT int这样的语句,用typedef一样可以完成,用哪个好呢?我主张用typedef,因为在早期的许多C编译器中这条语句是非法的,只是现今的编译器又做了扩充。为了尽可能地兼容,一般都遵循#define定义“可读”的常量以及一些宏语句的任务,而typedef则常用来定义关键字、冗长的类型的别名。
宏定义只是简单的字符串代换(原地扩展),而typedef则不是原地扩展,它的新名字具有一定的封装性,以致于新命名的标识符具有更易定义变量的功能。请看:
typedef (int*) pINT;
以及下面这行:
#define pINT2 int*
效果相同?实则不同!实践中见差别:pINT a,b;的效果同int *a; int *b;表示定义了两个整型指针变量。而pINT2 a,b;的效果同int *a, b;表示定义了一个整型指针变量a和整型变量b。
转自:typedef与宏定义区别
编程实训
题目:实现一个计算器,功能加减乘除
#include <stdio.h>
int main()
{
int num_1,num_2,result;
char run_num;
printf("输入需要运算的两个数,并用逗号隔开:\n");
scanf("%d,%d",&num_1,&num_2);
printf("输入运算符,如*,/,+,-\n");
getchar();
scanf("%c",&run_num);
switch(run_num)
{
case '+':
{
result = num_1 + num_2;
break;
}
case '-':
{
result = num_1 - num_2;
break;
}
case '*':
{
result = num_1 * num_2;
break;
}
case '/':
{
result = num_1 / num_2;
break;
}
default:
{
printf("error!");
}
}
printf("%d %c %d = %d",num_1,run_num,num_2,result);
return 0;
}
作业
- 设定输入长度
- 密码保护
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理