zl程序教程

您现在的位置是:首页 >  系统

当前栏目

Linux c下整数大小比较

Linux 比较 大小 整数
2023-09-11 14:18:25 时间


1. demo

今天分享一个Linux c下整数比较大小的一个小知识点,代码如下:

#include <stdio.h>
#include <stdlib.h>

#define INT_COMPARE(a, b)                \
        if(a > b){                       \
            printf( #a ">" #b "\n");     \
        }else if(a < b){                 \
            printf( #a "<" #b "\n");     \
        }else{                           \
            printf( #a "=" #b "\n");     \
        }                                


int main()
{
    //Case 1
    int a = -1;
    unsigned int b = 2;
    INT_COMPARE(a, b);

    //Case 2
    short c = -1;
    unsigned short d = 2;
    INT_COMPARE(c, d);

    //Case 3
    signed char e = -1;
    unsigned char f = 2;
    INT_COMPARE(e, f);

    //Case 4
    long g = -1;
    unsigned long h = 2;
    INT_COMPARE(g, h);

    return 0;
}

测试硬件条件:
Intel X86_64
ubuntu 20.04
大家猜猜看,各个结果是什么?

对于case1,大家应该在学校参加c语言校招面试题时,经常遇到这个题目吧,反正我当年是做过这个题:
当 int 和 unsigned int 比较时 int 会被转化为 unsigned int ,-1的值是将会是0xFFFFFFFF。
那我们简单的测试一下:

#include <stdio.h>

int main()
{
    int a = -1;
    printf("a = %x\n", (unsigned int)a);

    return 0;
}

在这里插入图片描述
0xFFFFFFFF是unsigned int的最大值,当然是 a >b;

那么对于其它的下面几个情况呢,让我们先来了解c标准中的整数提升。

2. 整数提升

让我们来看看C标准的类型转化:
C语言则允许在表达式中混合使用基本类型。在单个表达式中可以组合整数、浮点数,甚至是字符。当然,在这种情况下C编译器可能需要生成一些指令将这些操作数转换成相同的类型,使得硬件可以对表达式进行计算。

当类型转换出现在表达式时,可以将任何等级低于int(如:short 和 char)和unsigned int的类型转换为int(只要该类型的所有值都可以用int类型表示)
如有必要会被转换成unsigned int(如果short与int的大小相同,unsigned short就比int大,这种情况下,unsigned short会被转换成unsigned int),目前我们常用的x86_64和ARM体系架构(支持linux系统) int 都比 short 大,因此可以忽略。

C99允许每个整数类型具有“整数转换等级”。
下面按从最高级到最低级的顺序排列。
(1)unsigned long long
(2)long long
(3)unsigned long
(4)long
(5)unsigned int
(6)int
short和char类型没有列出,是因为它们已经被升级到 int 或 unsigned int。

类型转换时首先对两个操作数进行整数提升。如果这时两个操作数的类型相同,过程结束。否则,依次尝试下面的规则,一旦遇到可应用的规则就不再考虑别的规则:
(1) 如果两个操作数都是有符号型或者都是无符号型,将整数转换等级较低的操作数转换为等级较高的操作数的类型;
(2) 如果无符号操作数的等级高于或等于有符号操作数的等级,将有符号操作数转换为无符号操作数的类型。
(3) 如果有符号操作数类型可以表示无符号操作数类型的所有值,将无符号操作数转换为有符号操作数的类型。
(4) 否则,将两个操作数都转换为与有符号操作数的类型相对应的无符号类型。

说了这么多,那么我们就来针对上面的case 2 ,3 ,4 来解答一下:
case 2 , 3

short c = -1;
unsigned short d = 2;
  
signed char e = -1;
unsigned char f = 2;

short c 和 unsigned short d 都被转化为int 类型,实际比较是 (int) -1 和 (int) 2 进行比较,因此 c < d。
signed char e 和 unsigned char f 都被转化为int 类型,实际比较也是 (int) -1 和 (int) 2 进行比较,因此 e < f。

这里为什么单独char单独加上signed,因为 C标准规定char 为 Implementation Defined。
C标准表示char类型可以带符号也可以不带符号,由具体的编译器、处理器或其两者共同决定。
大部分体系架构 char 默认是带符号的。但是ARM体系架构 char 是不带符号的。

因此你使用了char类型,那么要保证在带符号和不带符号的情况下代码都没问题。如果不确定,请使用signed char 或 unsigned char明确表明它是哪个类型。

case 4

long g = -1;
unsigned long h = 2;

long g 和 unsigned long h 比较,long g 将被转化为 unsigned long ,因此和case 1情况一样,g > h。

那么我们测试一次我们结果是否正确吧:
可见结果正确。
在这里插入图片描述

3. 有符号数和无符号数之间的转化

同样字长的有符号数和无符号强制转换的结果保持位值不变,只是改变了解释这些位的方式。
因此C语言有条规则:处理同样字长的有符号数和无符号之间的相互转化,数值可能会变,但是位模式不变,即保存在内存中那个数值的二进制没有变化,只是解释的时候位代表的意义会不一样了。
注意这里我标明了是同样字长的有符号数和无符号。

4. linux内核中的 指针 和 unsigned long 有什么关系

1.指针的来源
方便引用一个内存地址。
给定一个内存地址,CPU就可以取出该地址的数据。
给定一个内存地址,CPU就可以写入该地址一个值。

2.指针的定义:
指针的本质其实也是一个数据类型,和char, short, int 等一样都属于c语言中的基本类型,所以大小也确定了(与它是什么类型无关),32位系统为4个字节,64为系统为8个字节。
无论指针什么类型(大小以确定),它存放的都是一个地址,只不过这个地址存放不同数据类型而已。
指针变量与其它变量(如int)相似,也有两个方面即:值 和 类型(所指向的数据类型)
值:表示某个对象的地址(位置)。
类型:表示那个地址(位置)上所存储对象的类型。

备注:指针的强制类型转换不会改变真实的指针,只是告诉编译器以新的数据类型来看待被指向的数据。

3.字长和数据类型
在支持Linux的体系结构中,能够由机器一次完成处理的数据称之为字,指明了指针数据的 nominal size。虚拟地址是以这样的一个字来编码,所以字长决定的最重要的系统参数就是虚拟地址空间的最大大小。即:对于一个字长位n的机器,虚拟地址的范围位 0 ~ 2n-1 ,程序最多访问2n个字节。

处理器通用寄存器的大小和他的字长是相同的。long类型总是等于机器的字长,也等于指针的大小。
但是long和int 大小没办法保证一样。比如:
Linux支持的64位体系架构 long 8个字节,int 4个字节。
Linux支持的32位体系架构 long 4个字节,int 4个字节
因此不要假设 long 和 int 长度相等,也不要假设指针和int 长度相等。
备注:上面只是讨论支持Linux的体系架构

4.Linux内核为什么常用unsigned long来替代指针?
因为指针也是一种基本类型,所以大小是确定的,32位系统下为4个字节,由于地址的值不能为负,所以指针的值的范围为 0 - (2^32-1),与unsigned long大小,值的范围一样。
所以32位系统下指针的最大寻址范围为2^32-1,即4G(所以32位系统虚拟地址为0 - 4G)。
64位系统下大小为8个字节,与unsigned long大小,值的范围一样。
可见指针与unsigned long 大小,值的范围也一样。(但意义不一样,只是值一样)

小结:指针与unsigned long 大小一样,值的范围也一样。
(当linux内核只关心这个地址的值,用于去运算、查找、对比,而不是关心通过这个地址去访问内存,这个时候,内核经常用unsigned long来替代指针)。

总结

这篇文章主要描述了Linux c下整数大小比较,以及linux内核中的 指针 和 unsigned long有什么关系。

参考资料

c primer plus
c 语言程序设计
深入理解计算机系统
Linux内核设计与实现
Linux环境编程:从应用到内核
为什么Linux内核常常用unsigned long来代替指针
聊聊C语言和指针的本质