嵌入式Linux C(二)——数据类型(详)

简介: 嵌入式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语言类型转换:不安全,可以将任何类型之间转换,有可能造成数据丢失(可以隐式输出,直接编译)

安全的类型转换:先检查两个类型是否可以转换

强制类型转换:(类型名称)变量名

如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如下图所示。


0a2653c851af460fa595bd959398a8f1.png


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;
}

0a2653c851af460fa595bd959398a8f1.png

小结:如何测量数据类型的大小


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

0a2653c851af460fa595bd959398a8f1.png

解决1:

2d65d23f6d4748949b924e4057485923.png

默认确实是有符号型,所以要看符号位

疑问点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;
}


作业


1.设定输入长度

2.密码保护

相关文章
|
8月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
449 15
|
网络协议 算法 Linux
【嵌入式软件工程师面经】Linux网络编程Socket
【嵌入式软件工程师面经】Linux网络编程Socket
315 1
|
9月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
385 13
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
640 3
|
Ubuntu 算法 Linux
嵌入式Linux的学习误区
**嵌入式Linux学习误区摘要** 1. **过度聚焦桌面Linux** - 许多学习者误将大量时间用于精通桌面Linux系统(如RedHat、Fedora、Ubuntu),认为这是嵌入式Linux开发的基石。 - 实际上,桌面Linux仅作为开发工具和环境,目标不应是成为Linux服务器专家,而应专注于嵌入式开发工具和流程。 2. **盲目阅读Linux内核源码** - 初学者在不了解Linux基本知识时试图直接研读内核源码,这往往导致困惑和挫败感。 - 在具备一定嵌入式Linux开发经验后再有针对性地阅读源码,才能有效提升技能。
180 4
|
传感器 人工智能 网络协议
:嵌入式 Linux 及其用途
【8月更文挑战第24天】
493 0
|
消息中间件 安全 Java
【嵌入式软件工程师面经】Linux多进程与多线程
【嵌入式软件工程师面经】Linux多进程与多线程
188 1
|
消息中间件 缓存 Unix
[面试必备]嵌入式Linux内核开发必须了解的三十道题
[面试必备]嵌入式Linux内核开发必须了解的三十道题
|
Linux Go 人机交互
嵌入式linux之go语言开发(十三)LittlevGL,漂亮的嵌入式GUI的go语言绑定
嵌入式linux之go语言开发(十三)LittlevGL,漂亮的嵌入式GUI的go语言绑定
|
存储 XML JSON
嵌入式linux之go语言开发(十二)参数配置文件存储模块开发
嵌入式linux之go语言开发(十二)参数配置文件存储模块开发