C语言笔记2

admin 2025年3月8日03:20:25评论2 views字数 4759阅读15分51秒阅读模式
C语言编译过程

C代码编译成可执行程序经过4步:

  • 1)预处理:宏定义展开、头文件展开、条件编译等,同时将代码中的注释删除,这里并不会检查语法;
  • 2)编译:检查语法,将预处理后文件编译生成汇编文件;
  • 3)汇编:将汇编文件生成目标文件(二进制文件);
  • 4)链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去;
C语言笔记2
在这里插入图片描述

然而一般编译的时候都是使用的一步编译。 (但是这样还是经过:预处理、编译、汇编、链接的过程。)

gcc hello.c -o hello
  • Linux
    下查找程序所依赖的动态库
ldd hello
CPU、寄存器
  • 寄存器是CPU内部最基本的存储单元;
  • CPU对外是通过总线(地址、控制、数据)来和外部设备交互的,总线的宽度是8位,同时CPU的寄存器也是8位,那么这个CPU就叫8位CPU
  • CPU计算时,先预先把要用的数据从硬盘读到内存,然后再把即将要用的数据读到寄存器。于是 CPU<--->寄存器<--->内存,这就是它们之间的信息交换,看下面的图片说明:

寄存器、缓存、内存三者关系:

关于VS的C4996错误
  • 由于微软在VS2013中不建议再使用C的传统库函数scanf,strcpy,sprintf等,所以直接使用这些库函数会提示C4996错误。VS建议采用带_s的函数,如scanf_sstrcpy_s,但这些并不是标准C函数。
  • 要想继续使用此函数,需要在源文件中添加以下两个指令中的一个就可以避免这个错误提示:
#define _CRT_SECURE_NO_WARNINGS     //这个宏定义最好要放到.c文件的第一行#pragmawarning(disable:4996)//或者使用这个

进制,原、反、补码

进制相关
#include<stdio.h>intmain(int argc, charconst *argv[])int a = 123;//十进制方式赋int b = 0123;//八进制方式赋值, 以数字0开int c = 0xABC;//十六进制方式赋值//如果在printf中输出一个十进制数那么用%d,八进制用%o,十六进制是%xprintf("十进制:%dn",a );printf("八进制:%on", b);// %o,为字母o,不是数字printf("十六进制:%xn", c);return0;}

输出:

十进制:123八进制:123十六进制:abc
原码、反码、补码
原码

一个数的原码(原始的二进制码)有如下特点:

  • 最高位做为符号位,0表示正,为1表示负;
  • 其它数值部分就是数值本身绝对值的二进制数;
  • 负数的原码是在其绝对值(相反数)的基础上,最高位变为1;

例如:(以一个字节(8bit)来看) 

十进制数
原码
+15
0000 1111
-15
1000 1111
+0
0000 0000
-0
1000 0000

原码存储导致2个问题:

  • 0有两种存储方式;
  • 正数和负数相加,结果不正确(计算机只会加不会减);

例如:以原码来算不同符号的数:

1-1 = 1 + -11: 00000001-1: 1000000110000010 = -2//答案错误
反码
  • 正数的原码和反码是一样;
  • 对于负数,先求原码,在原码基础上,符号位不变,其它位取反(0为1, 1变0);

例如:(以一个字节(8bit)来看) 

十进制数
原码
反码
+15
0000 1111
0000 1111
-15
1000 1111
1111 0000
+0
0000 0000
0000 0000
-0
1000 0000
1111 1111

反码计算两个符号不同的数:

1-1 = 1 + -1100000001-11111111011111111 = -0//答案是对的

但是反码还是没有解决0有两种存储方式的问题。

补码

综上,计算机存储数字以补码方式存储(为了解决负数的存储);

补码特点:

  • 对于正数,原码、反码、补码相同;
  • 对于负数,其补码为它的反码加1;

例如:(以一个字节(8bit)来看) 

十进制数
原码
反码
补码
+15
0000 1111
0000 1111
0000 1111
-15
1000 1111
1111 0000
1111 0001
+0
0000 0000
0000 0000
0000 0000
-0
1000 0000
1111 1111
0000 0000

补码计算:

1-1 = 1 + -1100000001-111111111100000000(最高位丢弃) = 00000000//注意1字节,所以丢弃最高位

记得一个原则:

  • 十进制数  -->  站在用户的角度  --> 原码;
  • 二进制、八进制、十六进制   -->  站在计算机角度  --> 补码;
原码和补码互换

原码求补码:

  • ①. 先求原码: 最高位符号位,其它位就是二进制;
  • ②. 在①基础上,符号位不变,其它位取反;
  • ③. 在②基础上加1;

补码求原码:

  • ①.  先得到补码;
  • ②.  求补码的反码,符号位不变,其它位取反;
  • ③.   在②基础上加1(注意不要人为的相乘相反是减)

综上,在计算机系统中,数值一律用补码来存储,主要原因是:

  • 统一了零的编码;
  • 将符号位和其它位统一处理;
  • 将减法运算转变为加法运算;
  • 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃;

相关案例计算转换:

#include<stdio.h>intmain(int argc, charconst *argv[]){char a = 0x81// 计算机的角度(补码)printf("a = %dn",a);char b = 0xe5;//计算机的角度(补码)printf("b = %dn",b);char c = 0x6f//计算机的角度(补码)printf("c = %dn",c);char d = -123//人类的角度 --> 看成原码printf("d = %xn",d);//输出16进制(计算机的角度)return0;}

输出:

a = -127b = -27c = 111d = ffffff85

对于上面程序的分析:

//二进制、八进制、十六进制,站在计算机角度,补码//0x81转为为二进制位1000 0001,最高位为1,说明是负数char a = 0x81;补码:10000001反码:11111110原码:11111111 = -127//10进制数,站在用户的角度,原码printf("%dn", a); //-127//二进制、八进制、十六进制,站在计算机角度,补码//0xe5二进制为1110 0101,最高位为1,说明是负数,它是负数的补码char b = 0xe5;补码:11100101反码:10011010原码:10011011 = -27//10进制数,站在用户的角度,原码printf("%dn", b);// -270x6f的二级制为01101111,最高位为0,它是正数char c = 0x6f;printf("%dn", c);// 111 原码、反码、补码都相同//10进制数,站在用户的角度,原码int a = -123;//注意这个是十进制,所以直接就是原码原码:10000000000000000000000001111011反码:11111111111111111111111110000100补码:11111111111111111111111110000101   f     f   f    f     f   f    85%x,默认以4个字节(32位)大小打印//二进制、八进制、十六进制,站在计算机角度,补码printf("%xn", a);// 16进制打印 ffffff85
有符号和无符号的区别
  • 有符号,最高位是符号位,如果是1代表为负数,如果为0代表为正数;
  • 无符号,最高位不是符号位,是数的一部分,无符号不可能是负数;

测试:

#include<stdio.h>intmain(int argc, charconst *argv[]){// 1000 0000 0000 0000 0000 0000 0111 1011// 8000007b// %d按照有符号数来打印  printf("%dn"0x8000007b);// %u按照无符号数打印printf("%un"0x8000007b);// signed 和 unsignedsignedint a = 10;unsignedint b = -10;//注意输出结果 以%d 为准,%d表示按照有符号数来输出    unsignedint c = 0x8000007b;printf("c[有符号] = %dn",c);printf("c[无符号] = %un",c);return0;}

输出:

-21474835252147483771c[有符号] = -2147483525c[无符号] = 2147483771

分析:

10000000000000000000000001111011//二进制8000007b   //16进制  //有符号   --->   %d, 默认以有符号的方式打印补码:10000000000000000000000001111011反码:11111111111111111111111110000100原码:11111111111111111111111110000101     - 7   f    f    f    f    f    85 = -2147483525-2147483525printf("%dn"0x8000007b); //-2147483525//无符号(最高位看做是数的一部分)  --->    %u, 以无符号的方式打印100000000000000000000000011110118000007b = 21474837712147483771printf("%un"0x8000007b); //2147483771
数据类型取值分析
数据类型范围(站在10进制角度,原码):char1个字节(8位,8bit)有符号的范围:正数:00000000 ~ 011111110127负数:10000000 ~ 11111111-0 ~ -127注意这里比较特使:-0 当做 -128使用-128原码:  110000000反码:  101111111补码:  110000000这个很特别: -128的原码和补码是一样的,发现和0 (10000000)也是一样的。无符号范围:00000000 ~ 111111110 ~ 255

综上,char:

  • 有符号:-128 ~ 127;
  • 无符号:0 ~ 255;
越界问题

赋值或者运算,记得不要越界,下面展示了越界的情况:

#include<stdio.h>intmain(int argc, charconst *argv[]){//有符号越界char a = 127 + 2;printf("a = %dn",a);  //-127//无符号越界unsignedchar b = 255 + 2;printf("b = %un",b);  //1return0;}

输出:

-1271

分析:

char a = 127 + 2;129转换为二进制:10000001,这是负数补码(计算机角度)补码:10000001反码:11111110原码:11111111(最高位是符号位) = -127printf("%dn", a);//-127unsignedchar b = 255 + 2257转化为二进制 :000100000001 (只取后8位)printf("%un", b);// 1

数据类型、运算符等基础

C语言数据类型
  • 数据类型的作用:编译器预算对象(变量)分配的内存空间大小。

「变量特点:」

  • 变量在编译时为其分配相应的内存空间;

  • 可以通过其名字和地址访问相应内存;

原文始发于微信公众号(泷羽sec-心安):C语言笔记2

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月8日03:20:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   C语言笔记2https://cn-sec.com/archives/3813980.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息