暄风吹斋响苍玉,竹石相看便有情。提胡卢来沽美酒,终日撩人不住声。

C语言学习笔记(二)

位运算

  • 指的是1个二进制数据的每一位来参与运算
  • 位运算的前提:是这个数必须是1个二进制
  • 参与位运算的二进制数据必须是补码形式
  • 位运算的结果也是二进制的补码形式

按位与:&

  1. 参与按位与的两个二进制数。如果都为1,那么结果就是1,只要有1位为0,那么结果就为0
  2. 先得到两个数的二进制补码形式
  3. 每一对应的数码进行比较
  4. 将得到的结果连成新的二进制
  5. 任何数按位与1的结果,永远等于这个数的最低位
  6. 可以用按位与判断一个数是否是奇数或偶数
    • 因为偶数的最低位一定为0,所以偶数按位与1的结果永远为0
    • 因为奇数的最低位一定为1,所以奇数按位与1的结果永远为1
    • 所以如果需要判断一个数是奇数还是偶数,可以将这个数按位与1来判断。如果为1,这个数是奇数;如果为0,这个数是偶数。
  7. 例子

    3 & 2

    3的补码:00000000 00000000 00000000 00000011
    2的补码:00000000 00000000 00000000 00000010
    结果的补码:00000000 00000000 00000000 00000010
    十进制结果:2

    -3 & 4

    -3的原码: 10000000 00000000 00000000 00000011
    -3的反码: 11111111 11111111 11111111 11111100
    -3的补码: 11111111 11111111 11111111 11111101
    4的补码: 00000000 00000000 00000000 00000100
    结果的补码:00000000 00000000 00000000 00000100
    十进制结果:4

    -3 & -4

    -4的原码: 10000000 00000000 00000000 00000100
    -4的反码: 11111111 11111111 11111111 11111011
    -4的补码: 11111111 11111111 11111111 11111100
    -3的补码: 11111111 11111111 11111111 11111101
    结果的补码:11111111 11111111 11111111 11111100
    结果的反码:11111111 11111111 11111111 11111011
    结果的原码:10000000 00000000 00000000 00000100
    十进制结果:-4

按位与的优先级比判断运算符低(==、>、<、…),所以如果需要做判断时应该用括号将算式括起来以改变运算顺序,如:(num & 1) == 0

按位或:|

  1. 参与按位与的两个二进制数。如果有一个为1则结果为1,两个值都为0,那么结果就为0
  2. 例子

    3 | 2

    3的补码: 00000000 00000000 00000000 00000011
    2的补码: 00000000 00000000 00000000 00000010
    结果的补码:00000000 00000000 00000000 00000011
    十进制结果:3

    -3 & 4

    -3的原码: 10000000 00000000 00000000 00000011
    -3的反码: 11111111 11111111 11111111 11111100
    -3的补码: 11111111 11111111 11111111 11111101
    4的补码: 00000000 00000000 00000000 00000100
    结果的补码:11111111 11111111 11111111 11111101
    结果的反码:11111111 11111111 11111111 11111100
    结果的原码:10000000 00000000 00000000 00000011
    十进制结果:-3

    -3 & -4

    -4的原码: 10000000 00000000 00000000 00000100
    -4的反码: 11111111 11111111 11111111 11111011
    -4的补码: 11111111 11111111 11111111 11111100
    -3的补码: 11111111 11111111 11111111 11111101
    结果的补码:11111111 11111111 11111111 11111101
    结果的反码:11111111 11111111 11111111 11111100
    结果的原码:10000000 00000000 00000000 00000011
    十进制结果:-3

按位取反: ~

  1. 单目运算符,将一个二进制数的每个数码取反。
  2. 例子

    ~3

    3的补码:00000000 00000000 00000000 00000011
    结果的补码:11111111 11111111 11111111 11111100
    结果的反码:11111111 11111111 11111111 11111011
    结果的原码:10000000 00000000 00000000 00000100
    十进制结果:-4

按位异或:^

  1. 比较两个二进制数的对应数位的数码,相同为0,不同为1
  2. 按位异或可以用来调换两个数的值:假设int a = 3,b = 2;
  3. 例子

    a = a ^ b = 3 ^ 2

    3的补码: 00000000 00000000 00000000 00000011
    2的补码: 00000000 00000000 00000000 00000010
    结果的补码:00000000 00000000 00000000 00000001
    十进制结果:a = 1

    b = a ^ b = 1 ^ 2

    1的补码: 00000000 00000000 00000000 00000001
    2的补码: 00000000 00000000 00000000 00000010
    结果的补码:00000000 00000000 00000000 00000011
    十进制结果:b = 3

    a = a ^ b = 1 ^ 3

    1的补码: 00000000 00000000 00000000 00000001
    3的补码: 00000000 00000000 00000000 00000011
    结果的补码:00000000 00000000 00000000 00000010
    十进制结果:a = 2

按位左移:<<

1
a << b
  1. 将二进制a向左移b个数位,高位溢出则舍弃,低位不够则补0
  2. 左移运算有可能改变a的正负性
  3. 将a左移b位,相当于将a乘以2的b次方
  4. 例子

3 << 2

3的补码:00000000 00000000 00000000 00000011
运算:00000000 00000000 00000000 0000001100
结果的补码:00000000 00000000 00000000 00001100
十进制结果:12
相当于:3 2^2 = 3 4 = 12

按位右移:>>

1
a >> b
  1. 将二进制a向右移b位,低位溢出则舍弃,高位不足补符号位
  2. 右移运算不会改变a的正负性
  3. 将a右移b位,相当于将a除以2的b次方(商)
  4. 例子

    3 >> 2

    3的补码: 00000000 00000000 00000000 00000011
    运算: 0000000000 00000000 00000000 00000011
    结果的补码:00000000 00000000 00000000 00000000
    十进制结果:0
    相当于:3 / 2^2 = 3 / 4 = 0

    -3 >> 4

    -3的补码: 11111111 11111111 11111111 11111101
    运算: 111111111111 11111111 11111111 11111101
    结果的补码:11111111 11111111 11111111 11111111
    结果的反码:11111111 11111111 11111111 11111110
    结果的原码:10000000 00000000 00000000 00000001
    十进制结果:-1
    相当于:???

内存中的五大区域

前提

  1. 不管哪个区域,都是用来存储数据的
  2. 不同类型的数据存储在不同的区域,便于系统的管理

五大区域

  1. 用来存储局部变量,所有的局部变量全部都声明在栈区域中

  2. 允许程序员手动申请堆中的空间进行使用

  3. BSS段

    用来存储未初始化的全局变量与静态变量

  4. 数据段(常量区)

    用来存储已初始化的全局变量与静态变量以及常量数据

  5. 代码段

    用来存储程序的代码/指令

字符串

如何存储字符串

  • 原理:

    将字符串数据的每1个字符存储到字符数组中,并在后面追加1个“\0”代表字符串存储完毕

  • 最根本的存储方法:

    1
    char name[5] = {‘j’, ‘a’, ‘c’, ‘k’, ‘\0’};

    将字符串的每一个字符存储到字符数组中,在后面追加一个“\0”代表存储结束

  • 简写方式:

    1
    char name[] = {“jack”};

    系统自动的会将这个字符串中的每一个字符存储到字符数组中,并自动追加一个“\0”

  • 最常用的方式:

    1
    char name[] = “jack”;

    等同于简写的方式

如何输出字符串

使用%s来输出字符串变量,其原理是:

从给定的数组的地址开始,逐个字节的读取并显示,直到遇到“\0”字节,结束。

如何接收用户输入的字符串

  • scanf( )函数
    1. 定义一个字符数组用来接取输入字符串
    2. 使用%s来定义用户输入的是一个字符串值
  • 注意
    1. 接取输入内容的字符数组的长度必须比输入字符长度大
    2. 输入内容时不可以输入空格,否则系统会认为输入结束,空格后的内容将无法接收

计算字符串的长度

不可用sizeof去计算字符数组的长度来得到字符串的长度,因为有可能字符串数据存储在字符数组中只占了一部分,正确的计算方式为:

从第一个字节开始计数,直到遇到“\0”结束。

字符串常用函数

stdio.h文件下的函数:

  1. puts( )函数
    • 格式:puts(字符数组名);
    • 作用:用来输出字符串
    • 优点:输出完成后自动换行
    • 缺点:只能输出字符串
  2. gets( )函数
    • 格式:gets(用来保存字符串的数组);
    • 作用:接收用户输入并赋值给给定的字符数组
    • 优点:相较于scanf函数,gets函数可以接收空格字符
    • 缺点:不安全
  3. fputs( )函数
    • 格式:fputs(要输出的字符串,指定的流);
    • 作用:将字符串数据输出到指定的流中
    • 流:
      • 标准输出流 —> 控制台,指定的流:stdout
      • 文件流 —> 磁盘上的文件
    • 先声明一个文件指针,指向磁盘上的文件:FILE *pfile = fopen(指向的文件路径,操作文件的模式)
      • 操作模式:
        • w —> 写,当文件不存在则创建;存在则覆盖
        • r —> 读
        • a —> 追加输入,在文件的最后面追加内容
      • 注意:输出完成后需要使用fclose(文件指针)关闭文件。
  4. fgets( )函数
    • 格式:fgets( );
    • 参数:
      • 字符指针:存储读取的字符床
      • int:最多接收的字符串的长度,实际接收长度为n-1个,还有1个被“\0”占用了
      • 3.指定的流
    • 作用:读取指定的流中的内容
    • 比较:
      • scanf:不安全,输入的空格会被认为结束
      • gets:不安全
      • fgets:安全,空格也会接受
    • 流:
      • 标准输入流 —> 控制台,指定的流:stdin
      • 文件流 —> 磁盘上的文件
    • 注意:输出完成后需要使用fclose(文件指针)关闭文件。

string.h文件下的函数:

  1. strlen( )函数
    • 格式:int len = strlen(字符数组名);
    • 作用:得到存储在字符数组中的字符串数据的长度
  2. strcmp()函数
    • cmp —> compare 比较
    • 格式:int res = strcmp(字符数组1,字符数组2);
    • 作用:用来比较两个字符串的大小
    • 返回值:
      • 如果返回的是负数,说明字符串1比字符串2小
      • 如果返回的是证书,说明字符串1比字符串2大
      • 如果返回的是0,说明一样
    • 比较规则:比的是相同位置的字符的ASCII码的大小
  3. strcpy( )函数
    • cpy —> copy 拷贝
    • 格式:strcpy(字符数组1,字符数组2);
    • 作用:用来将字符数组2的字符串拷贝到字符数组1中
    • 可能的问题:存储字符串1的字符数组长度不够,无法存储字符串2,这个时候会报错
  4. strcat()函数
    • cat —> concat 连接
    • 格式:strcat(字符数组1,字符数组2);
    • 作用:把存储在字符数组2中的字符串数据连接到字符串1的后面
    • 可能的问题: 存储字符串1的数组剩余的长度不足以存储字符串2时,会报错

指针与字符串数据

可以使用字符指针来存储字符串数据

1
char *name = “jack";

指针存储字符串与字符数组存储字符串的区别

1
2
char name1[] = "jack";
char *name2 = “jack”;
  1. 当他们作为局部变量时:
    • name1字符数组是申请在栈区域,字符串的每一个字符存储在这个字符数组的每一个元素中
    • name2指针变量是申请在栈区域,字符串数据是以字符数组的形式存储在常量区中,name2指针变量中存储的是字符串在常量区中的地址
  2. 当他们作为全局变量时:
    • name1字符数组是申请在常量区,字符串的每1个字符是存储在这个数组的每一个元素中
    • name2指针变量是申请在常量区,字符串数据也是以字符数组的形式存储在常量区中,name2指针变量中存储的是字符串在常量区中的地址
  3. 区别:
    • 区别1:
      • 以字符数组存储时:无论如何都是1个字符数组,并且字符串的每一个字符存储在数组的元素中
      • 以字符指针存储时:无论如何首先都有1个指针变量,字符串数据是以字符数组的形式存储在常量区
    • 区别2:可变与不变性
      • 以字符数组存储的字符串,无论全局还是局部变量,都可以使用下标修改字符数组元素的值
      • 以字符指针的形式存储字符串数据,无论全局还是局部变量,都字符指针说指向的字符数组的值是不可修改的

字符串的恒定性

  1. 大前提:以字符指针形式存储的字符串
  2. 字符指针形式存储的字符串,其字符串数据必定是存储在常量区中,且不可更改
  3. 当我们以字符指针的形式要将字符串数据存储到常量区的时候,并不是直接将字符串存储到常量区中,而是先检查常量区中是否已经存在相同内容的字符串,如果存在则直接将字符指针指向现有的字符串地址,如果不存在才会将这个字符串数据存储到常量区中。
  4. 注意:当重新为字符指针初始化1个字符串时,并不是修改原有字符串,而是重新创建了一个字符串,并将指针指向新字符串,所以这种情况并不违背字符串的恒定性

以字符指针形式存储字符串的优势

  1. 以字符数组存储的字符串数据,长度固定,一旦创建后最多就只能存储这么长的支付数据了;而以字符指针的形式存储的字符串长度不限
  2. 修改显示的值,字符指针比字符数组更便捷

字符串数组

1
char *names[] = {“jack”,”rose”};

使用指针数组存储字符串数组的优势

每1个字符串的长度没有限制,而二维数组存储字符串数组时,每1个字符串的长度不能超过二维数组的列数-1

字符串数组的排序

将字符串数组当中的字符串以字母的形式排序

  1. 冒泡排序
  2. 使用strcmp( )函数比较两个字符串的大小
-------------本文结束感谢您的阅读-------------
坚持原创技术及精品文章分享,您的支持将鼓励我继续创作!
0%