注册 登录
编程论坛 C语言论坛

此帖为质疑讨论帖,知识碰撞帖,指导贴,由我回答某人问题被质疑开展开的

叶纤 发布于 2020-03-20 11:20, 5162 次点击
此贴只讨论知识不要有个人情感
事件起因
由回答此帖的第二个问题开始的的讨论
https://bbs.bccn.net/redirect.php?goto=findpost&pid=2722914&ptid=500418
因不想自私的占用提问者的帖子,特新开一贴,继续讨论,知识难免会有碰撞,改变误区还是要贵在讨论
总结了一下,无符号数字在进行运算的时候虽然会出现陷阱,比如无符号没有负数的情况,如果在运算的时候出现负数那么就会出现很大的数,二如果溢出就会出现回绕现象,
在本帖中负数的情况没人讨论,对于溢出的情况到是有几个观点
第一个观点,如果无符号数字出现溢出就更换大的数据类型,可以是无符号类型
第二个观点,无符号类型可装的数据比有符号类型大,相对有符号更不易溢出
其他大部分都在讨论溢出和环绕的区别,以及为什么避免使用无符号类型?这里我要喊冤,本人一次没有说过避免使用无符号类型,而是说无符号类型存在陷阱,避免使用无符号数字
不过还是不建议在加减运算中使用无符号,出现负数就好玩了
还有几贴回答值得学习的慢慢吸收

[此贴子已经被作者于2020-3-20 23:57编辑过]

37 回复
#2
lin51616782020-03-20 13:09
你说要避免使用无符号类型
理由是无符号数会回绕
并给出一个例子 (unsigned char)280 结果是 24
所以要避免

问题是 难道有符号类型没有回绕现象?
按照这个例子使用有符号类型 效果完全一样是 24
为什么无符号类型就需要避免使用 而有符号数就不需要避免使用了?
#3
纯蓝之刃2020-03-20 13:23
我倒是认为应该尽量使用无符号类型。
因为对于unsigned整型溢出,C的规范是有定义的——“溢出后的数会以2^(8*sizeof(type))作模运算”。
对于signed整型的溢出,C的规范定义是“undefined behavior”。
所以有符号整形会将溢出现象交给编译器处理,不同的编译器会有不同的处理结果。
同时在一般使用时,有符号类型会在往无符号类型转换时和动态分配内存时造成不可预知的错误。
https://blog.
#4
叶纤2020-03-20 13:43
无符号类型有它的优点,但是无符号类型也存在陷阱,所以并不是让你完全禁止使用,而是除非在特殊情况下或不可避免,否则请避免使用无符号数字。
1 那么使用无符号数字在哪里合理呢?
   
首先,在进行位操作时,首选无符号数字
其次,在某些情况下仍然不可避免地要使用无符号数,主要是那些与数组索引有关的数字。
另请注意,如果您是为嵌入式系统(例如Arduino)或其他处理器/内存受限的环境而开发的,出于性能方面的考虑,无符号数的使用更为常见并被接受(在某些情况下是不可避免的)。
2 为什么说避免使用
    当不需要负数时,无符号整数非常适合于网络和内存很少的系统,因为无符号整数可以存储更多的正数而不会占用额外的内存。
无符号整数不会溢出。 取而代之的是,如果值超出范围,则将其除以该类型的最大数除以一,然后仅保留其余部分。也就是回绕
举个例子     数字280太大,无法容纳0到255的1字节范围。大于类型的最大数字的1是256。因此,我们将280除以256,得到1个余数24。24的余数 被储存了。
程序代码:
        #include <iostream>
     
    int main()
    {
        unsigned short x{ 65535 }; // largest 16-bit unsigned value 2*8。 16possible
        std::cout << "x was: " << x << '\n';
     
        x = 65536; // 65536 is out of our range, so we get wrap-around65536超出了我们的范围,因此我们得到了环绕
        std::cout << "x is now: " << x << '\n';0
     
        x = 65537; // 65537 is out of our range, so we get wrap-around65537超出了我们的范围,因此我们得到了环绕。    1
        std::cout << "x is now: " << x << '\n';
     
        return 0;
    }

程序代码:
    避免使用无符号
    #include <iostream>
     
    int main()
    {
    unsigned int x{ 3 };
    unsigned int y{ 5 };
     
    std::cout << x - y << '\n';
    return 0;
    }



    发生这种情况的原因是-2环绕到一个数字,该数字接近4字节整数范围的顶部。 当使用-运算符反复减小无符号整数时,通常会发生不必要的回绕。 引入循环后,您将看到一个示例。
    •  第二,混合有符号和无符号整数时,可能会导致意外行为。 在上面的示例中,即使其中一个操作数(x或y)是有符号的,另一个操作数(无符号的一个)也将导致有符号的一个被提升为无符号的整数,并且将产生相同的行为!
    • 除非在特殊情况下或不可避免,否则请避免使用无符号数字。
当然以上内容是我的学习笔记,有很多地方都不明白,如果对
无符号是陷阱还有疑问的话,请参阅http://soundsoftware.ac.uk/c-pitfall-unsigned.html
#5
叶纤2020-03-20 13:44
你们对无符号数字有什么疑问
#6
叶纤2020-03-20 13:50
当然以上是我的学习笔记纯属复制粘贴,也有很多自己删除的部分如果想看原文我可以给您们
#7
叶纤2020-03-20 13:52
4.5 — Unsigned integers, and why to avoid them
BY ALEX ON APRIL 23RD, 2019 | LAST MODIFIED BY NASCARDRIVER ON MARCH 17TH, 2020
Unsigned integers

In the previous lesson (4.4 -- Signed integers), we covered signed integers, which are a set of types that can hold positive and negative whole numbers, including 0.

C++ also supports unsigned integers. Unsigned integers are integers that can only hold non-negative whole numbers.

Defining unsigned integers

To define an unsigned integer, we use the unsigned keyword. By convention, this is placed before the type:

1
2
3
4
unsigned short us;
unsigned int ui;
unsigned long ul;
unsigned long long ull;
Unsigned integer range

A 1-byte unsigned integer has a range of 0 to 255. Compare this to the 1-byte signed integer range of -128 to 127. Both can store 256 different values, but signed integers use half of their range for negative numbers, whereas unsigned integers can store positive numbers that are twice as large.

Here’s a table showing the range for unsigned integers:

Size/Type    Range
1 byte unsigned    0 to 255
2 byte unsigned    0 to 65,535
4 byte unsigned    0 to 4,294,967,295
8 byte unsigned    0 to 18,446,744,073,709,551,615
An n-bit unsigned variable has a range of 0 to (2n)-1.

When no negative numbers are required, unsigned integers are well-suited for networking and systems with little memory, because unsigned integers can store more positive numbers without taking up extra memory.

Remembering the terms signed and unsigned

New programmers sometimes get signed and unsigned mixed up. The following is a simple way to remember the difference: in order to differentiate negative numbers from positive ones, we use a negative sign. If a sign is not provided, we assume a number is positive. Consequently, an integer with a sign (a signed integer) can tell the difference between positive and negative. An integer without a sign (an unsigned integer) assumes all values are positive.

Unsigned integer overflow

Trick question: What happens if we try to store the number 280 (which requires 9 bits to represent) in a 1-byte unsigned integer? You might think the answer is “overflow!”. But, it’s not.

By definition, unsigned integers cannot overflow. Instead, if a value is out of range, it is divided by one greater than the largest number of the type, and only the remainder kept.


 
The number 280 is too big to fit in our 1-byte range of 0 to 255. 1 greater than the largest number of the type is 256. Therefore, we divide 280 by 256, getting 1 remainder 24. The remainder of 24 is what is stored.

Here’s another way to think about the same thing. Any number bigger than the largest number representable by the type simply “wraps around” (sometimes called “modulo wrapping”). 255 is in range of a 1-byte integer, so 255 is fine. 256, however, is outside the range, so it wraps around to the value 0. 257 wraps around to the value 1. 280 wraps around to the value 24.

Let’s take a look at this using 2-byte integers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
 
int main()
{
    unsigned short x{ 65535 }; // largest 16-bit unsigned value possible
    std::cout << "x was: " << x << '\n';
 
    x = 65536; // 65536 is out of our range, so we get wrap-around
    std::cout << "x is now: " << x << '\n';
 
    x = 65537; // 65537 is out of our range, so we get wrap-around
    std::cout << "x is now: " << x << '\n';
 
    return 0;
}
What do you think the result of this program will be?

x was: 65535
x is now: 0
x is now: 1
It’s possible to wrap around the other direction as well. 0 is representable in a 1-byte integer, so that’s fine. -1 is not representable, so it wraps around to the top of the range, producing the value 255. -2 wraps around to 254. And so forth.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
 
int main()
{
    unsigned short x{ 0 }; // smallest 2-byte unsigned value possible
    std::cout << "x was: " << x << '\n';
 
    x = -1; // -1 is out of our range, so we get wrap-around
    std::cout << "x is now: " << x << '\n';
 
    x = -2; // -2 is out of our range, so we get wrap-around
    std::cout << "x is now: " << x << '\n';
 
    return 0;
}
x was: 0
x is now: 65535
x is now: 65534
Author's note

In common language, unsigned integer wrap around is often incorrectly called “overflow” since the cause is identical to signed integer overflow.
As an aside...

Many notable bugs in video game history happened due to wrap around behavior with unsigned integers. In the arcade game Donkey Kong, it’s not possible to go past level 22 due to a bug that leaves the user with not enough bonus time to complete the level. In the PC game Civilization, Gandhi was known for being the first one to use nuclear weapons, which seems contrary to his normally passive nature. Gandhi’s aggression setting was normally set at 1, but if he chose a democratic government, he’d get a -2 modifier. This wrapped around his aggression setting to 255, making him maximally aggressive!
The controversy over unsigned numbers

Many developers (and some large development houses, such as Google) believe that developers should generally avoid unsigned integers.

This is largely because of two behaviors that can cause problems.

First, consider the subtraction of two unsigned numbers, such as 3 and 5. 3 minus 5 is -2, but -2 can’t be represented as an unsigned number.

1
2
3
4
5
6
7
8
9
10
#include <iostream>
 
int main()
{
    unsigned int x{ 3 };
    unsigned int y{ 5 };
 
    std::cout << x - y << '\n';
    return 0;
}
On the author’s machine, this seemingly innocent looking program produces the result:

1
4294967294
This occurs due to -2 wrapping around to a number close to the top of the range of a 4-byte integer. A common unwanted wrap-around happens when an unsigned integer is repeatedly decremented with the -- operator. You’ll see an example of this when loops are introduced.

Second, unexpected behavior can result when you mix signed and unsigned integers. In the above example, even if one of the operands (x or y) is signed, the other operand (the unsigned one) will cause the signed one to be promoted to an unsigned integer, and the same behavior will result!

Consider the following snippet:

1
2
3
4
5
6
7
8
9
10
11
12
13
void doSomething(unsigned int x)
{
    // Run some code x times
 
    std::cout << "x is " << x << '\n';
}
 
int main()
{
    doSomething(-1);
 
    return 0;
}
The author of doSomething() was expecting someone to call this function with only positive numbers. But the caller is passing in -1. What happens in this case?

The signed argument of -1 gets implicitly converted to an unsigned parameter. -1 isn’t in the range of an unsigned number, so it wraps around to some large number (probably 4294967295). Then your program goes ballistic. Worse, there’s no good way to guard against this condition from happening. C++ will freely convert between signed and unsigned numbers, but it won’t do any range checking to make sure you don’t overflow your type.

If you need to protect a function against negative inputs, use an assertion or exception instead. Both are covered later.

Some modern programming languages (such as Java) and frameworks (such as .NET) either don’t include unsigned types, or limit their use.

New programmers often use unsigned integers to represent non-negative data, or to take advantage of the additional range. Bjarne Stroustrup, the designer of C++, said, “Using an unsigned instead of an int to gain one more bit to represent positive integers is almost never a good idea”.

Warning

Avoid using unsigned numbers, except in specific cases or when unavoidable.

Don’t avoid negative numbers by using unsigned types. If you need a larger range than a signed number offers, use one of the guaranteed-width integers shown in the next lesson (4.6 -- Fixed-width integers and size_t).

If you do use unsigned numbers, avoid mixing signed and unsigned numbers where possible.
So where is it reasonable to use unsigned numbers?

There are still a few cases in C++ where it’s okay (or necessary) to use unsigned numbers.

First, unsigned numbers are preferred when dealing with bit manipulation (covered in chapter O).

Second, use of unsigned numbers is still unavoidable in some cases, mainly those having to do with array indexing. We’ll talk more about this in the lessons on arrays and array indexing.

Also note that if you’re developing for an embedded system (e.g. an Arduino) or some other processor/memory limited context, use of unsigned numbers is more common and accepted (and in some cases, unavoidable) for performance reasons.
#8
叶纤2020-03-20 14:00
当然以上内容我可能用翻译软件翻译的不到位,那请你们指出哪里翻译的不好,我知道不论是有符号还是无符号类型,整数都有可能益处,不过面对无符号的数字益出我更喜欢用回绕这个词,有符号的数字益处有可能是—526724835739这样的数字
所以面对整数益出网站上也有一些评论
整数溢出

如果我们尝试将值280分配给1个字节的有符号整数会怎样?此数字超出1字节有符号整数可以容纳的范围。数字280需要表示9位(加上1个符号位),但是在1字节有符号整数中,我们只有7位(加上1个符号位)可用。

当我们尝试存储超出类型范围的值时,会发生整数溢出(通常简称为溢出)。本质上,我们要存储的数字表示的位数比对象可用的位数更多。在这种情况下,数据丢失是因为对象没有足够的内存来存储所有内容。

在有符号整数的情况下,丢失哪些位的定义不明确,因此有符号整数溢出会导致未定义的行为。

警告

有符号整数溢出将导致不确定的行为。
通常,溢出会导致信息丢失,这几乎是不希望的。如果有任何怀疑的对象可能需要存储落在其范围之外的值,使用类型有一个更大的范围内!

整数除法
英文版
Integer overflow

What happens if we try to assign the value 280 to a 1-byte signed integer? This number is outside the range that a 1-byte signed integer can hold. The number 280 requires 9 bits (plus 1 sign bit) to be represented, but we only have 7 bits (plus 1 sign bit) available in a 1-byte signed integer.

Integer overflow (often called overflow for short) occurs when we try to store a value that is outside the range of the type. Essentially, the number we are trying to store requires more bits to represent than the object has available. In such a case, data is lost because the object doesn’t have enough memory to store everything.

In the case of signed integers, which bits are lost is not well defined, thus signed integer overflow leads to undefined behavior.

Warning

Signed integer overflow will result in undefined behavior.
In general, overflow results in information being lost, which is almost never desirable. If there is any suspicion that an object might need to store a value that falls outside its range, use a type with a bigger range!
#9
lin51616782020-03-20 14:10
回复 7楼 叶纤
无符号类型我很熟悉 不需要看你的笔记

你说要避免使用无符号类型 却不能解释清楚为什么
目前只看到有一个所谓的理由是 回绕
(signed char)280 结果是 24
3U - 5U 结果是一个非常大的整数

问题是这个现象 有符号类型/无符号类型都会出现
无非是出现这个现象的数值不同
这样的理由不足以用于说明 为什么要避免使用无符号类型
#10
lin51616782020-03-20 14:13
以下是引用叶纤在2020-3-20 14:00:07的发言:

当然以上内容我可能用翻译软件翻译的不到位,那请你们指出哪里翻译的不好,我知道不论是有符号还是无符号类型,整数都有可能益处,不过面对无符号的数字益出我更喜欢用回绕这个词,有符号的数字益处有可能是—526724835739这样的数字
所以面对整数益出网站上也有一些评论
整数溢出

如果我们尝试将值280分配给1个字节的有符号整数会怎样?此数字超出1字节有符号整数可以容纳的范围。数字280需要表示9位(加上1个符号位),但是在1字节有符号整数中,我们只有7位(加上1个符号位)可用。

当我们尝试存储超出类型范围的值时,会发生整数溢出(通常简称为溢出)。本质上,我们要存储的数字表示的位数比对象可用的位数更多。在这种情况下,数据丢失是因为对象没有足够的内存来存储所有内容。

在有符号整数的情况下,丢失哪些位的定义不明确,因此有符号整数溢出会导致未定义的行为。

警告

有符号整数溢出将导致不确定的行为。
通常,溢出会导致信息丢失,这几乎是不希望的。如果有任何怀疑的对象可能需要存储落在其范围之外的值,使用类型有一个更大的范围内!

整数除法
英文版
Integer overflow

What happens if we try to assign the value 280 to a 1-byte signed integer? This number is outside the range that a 1-byte signed integer can hold. The number 280 requires 9 bits (plus 1 sign bit) to be represented, but we only have 7 bits (plus 1 sign bit) available in a 1-byte signed integer.

Integer overflow (often called overflow for short) occurs when we try to store a value that is outside the range of the type. Essentially, the number we are trying to store requires more bits to represent than the object has available. In such a case, data is lost because the object doesn’t have enough memory to store everything.

In the case of signed integers, which bits are lost is not well defined, thus signed integer overflow leads to undefined behavior.

Warning

Signed integer overflow will result in undefined behavior.
In general, overflow results in information being lost, which is almost never desirable. If there is any suspicion that an object might need to store a value that falls outside its range, use a type with a bigger range!

这一层的内容可以这样解读
如果会出现溢出现象 应该使用更大类型
如果不使用更大类型 也应该考虑使用无符号类型 而不使用有符号类型
因为 有符号类型溢出是未定义行为 无符号类型溢出保证是回绕

这同样不是一个 避免使用无符号类型的理由


[此贴子已经被作者于2020-3-20 14:14编辑过]

#11
叶纤2020-03-20 14:26
如果对有符号类型和无符号数字类型的数字益出还不明白那我举个例子
程序代码:

#include<iostream>
int main()
{

     short ch = 32768;
    unsigned short ch1=65536;
     int ch2=2147483648;
     unsigned int ch_2=4294967296;
   std::cout<<"32位short有符号益出"<<ch<<'\n';
   std::cout<<"32位short无符号益出"<<ch1<<'\n';
    std::cout<<"32位int有符号益出"<<ch2<<'\n';
    std::cout<<"32位int无符号益出"<<ch_2<<'\n';
}

32位short有符号益出-32768
32位short无符号益出0
32位int有符号益出-2147483648
32位int无符号益出0
#12
lin51616782020-03-20 14:28
回复 11楼 叶纤
你看不懂我在说什么 不是我看不懂你在说什么
我知道溢出
我是说溢出不能作为避免使用无符号类型的理由
#13
lin51616782020-03-20 14:29
回复 11楼 叶纤
有符号类型 无符号类型 都会溢出
为什么无符号就要避免 而有符号就不用
这才是我的提问
你闹半天都没搞清楚重点吗?
#14
return_02020-03-20 14:30
这个帖子好吵呀
#15
叶纤2020-03-20 14:35
以下是引用lin5161678在2020-3-20 14:13:05的发言:


这一层的内容可以这样解读
如果会出现溢出现象 应该使用更大类型
如果不使用更大类型 也应该考虑使用无符号类型 而不使用有符号类型
因为 有符号类型溢出是未定义行为 无符号类型溢出保证是回绕

这同样不是一个 避免使用无符号类型的理由

你所引用且翻译的是有符号的教学内容,并没有学到无符号的知识点,而且老师教的是当数字益出了,底层是怎么运作的,我所表达的也是和数字益出有关
#16
叶纤2020-03-20 14:41
以下是引用lin5161678在2020-3-20 14:29:27的发言:

有符号类型 无符号类型 都会溢出
为什么无符号就要避免 而有符号就不用
这才是我的提问
你闹半天都没搞清楚重点吗?

大佬可能还没明白我所表达的内容,我从头到尾就没说避免使用无符号的类型
无符号类型有它的优点,但是无符号类型也存在陷阱,所以并不是让你完全禁止使用,而是除非在特殊情况下或不可避免,否则请避免使用无符号数字。

我说的是无符号的数字
#17
lin51616782020-03-20 14:42
是不是教学内容和这里的讨论无关
这里讨论的是 避免使用无符号类型的理由是什么
你目前的所有回复有不能讲清楚这个理由
#18
叶纤2020-03-20 14:42
无符号类型有它的优点,但是无符号类型也存在陷阱,所以并不是让你完全禁止使用,而是除非在特殊情况下或不可避免,否则请避免使用无符号数字。
1 那么使用无符号数字在哪里合理呢?
   
首先,在进行位操作时,首选无符号数字
其次,在某些情况下仍然不可避免地要使用无符号数,主要是那些与数组索引有关的数字。
另请注意,如果您是为嵌入式系统(例如Arduino)或其他处理器/内存受限的环境而开发的,出于性能方面的考虑,无符号数的使用更为常见并被接受(在某些情况下是不可避免的)。
2 为什么说避免使用
    当不需要负数时,无符号整数非常适合于网络和内存很少的系统,因为无符号整数可以存储更多的正数而不会占用额外的内存。
无符号整数不会溢出。 取而代之的是,如果值超出范围,则将其除以该类型的最大数除以一,然后仅保留其余部分。也就是回绕
举个例子     数字280太大,无法容纳0到255的1字节范围。大于类型的最大数字的1是256。因此,我们将280除以256,得到1个余数24。24的余数 被储存了。
程序代码:
        #include <iostream>
     
    int main()
    {
        unsigned short x{ 65535 }; // largest 16-bit unsigned value 2*8。 16possible
        std::cout << "x was: " << x << '\n';
     
        x = 65536; // 65536 is out of our range, so we get wrap-around65536超出了我们的范围,因此我们得到了环绕
        std::cout << "x is now: " << x << '\n';0
     
        x = 65537; // 65537 is out of our range, so we get wrap-around65537超出了我们的范围,因此我们得到了环绕。    1
        std::cout << "x is now: " << x << '\n';
     
        return 0;
    }

程序代码:
    避免使用无符号
    #include <iostream>
     
    int main()
    {
    unsigned int x{ 3 };
    unsigned int y{ 5 };
     
    std::cout << x - y << '\n';
    return 0;
    }



    发生这种情况的原因是-2环绕到一个数字,该数字接近4字节整数范围的顶部。 当使用-运算符反复减小无符号整数时,通常会发生不必要的回绕。 引入循环后,您将看到一个示例。
      第二,混合有符号和无符号整数时,可能会导致意外行为。 在上面的示例中,即使其中一个操作数(x或y)是有符号的,另一个操作数(无符号的一个)也将导致有符号的一个被提升为无符号的整数,并且将产生相同的行为!
     除非在特殊情况下或不可避免,否则请避免使用无符号数字。
当然以上内容是我的学习笔记,有很多地方都不明白,如果对
无符号是陷阱还有疑问的话,请参阅http://soundsoftware.ac.uk/c-pitfall-unsigned.html
争论内容在这
#19
叶纤2020-03-20 14:56
以下是引用lin5161678在2020-3-20 14:42:05的发言:

是不是教学内容和这里的讨论无关
这里讨论的是 避免使用无符号类型的理由是什么
你目前的所有回复有不能讲清楚这个理由

一,这个是我发出的主题帖,并不是你发的主题帖,而且发这则主题帖的原因是由4楼内容开展的,你只是对我的内容有质疑,我也是把我发帖子的思想展现给您
二,汉字部分已经很清楚了,我所讨论的不是避免无符号类型,而是以下内容
是无符号类型有它的优点,但是无符号类型也存在陷阱,所以并不是让你完全禁止使用,而是除非在特殊情况下或不可避免,否则请避免使用无符号数字。

#20
纯蓝之刃2020-03-20 15:20
你在4楼只是给出了无符号数的溢出现象而没有给出有符号数的溢出现象。
相反我认为你在4楼所得到的结论正好是无符号数的优点,而应该避免使用有符号数。
因为无符号数回转之后还是一个正整数,并且这个正整数是可以预知的值,顶多会造成死循环,反而我们可以在得到溢出值后对该值进行准确的计算并判断该值是否溢出。
而有符号数回转之后返回的是一个不确定的值,我们无从判断该值是溢出后的值还是正常计算得到的值,反而无法对溢出现象进行判断更谈不上对该现象的纠正。
这样就会得到malloc((-n)*sizeof(char*);a[-n];等造成内存非法访问等比程序跑飞更加极度危险的情况。
所以按照你在4楼的论据得到的结论应该是“在数据类型允许的情况下,应该尽量使用无符号数字。”
同时在我3楼的帖子中给出的无符号数据在按位操作时更具有优越性。
#21
叶纤2020-03-20 16:29
以下是引用纯蓝之刃在2020-3-20 15:20:33的发言:

你在4楼只是给出了无符号数的溢出现象而没有给出有符号数的溢出现象。
相反我认为你在4楼所得到的结论正好是无符号数的优点,而应该避免使用有符号数。
因为无符号数回转之后还是一个正整数,并且这个正整数是可以预知的值,顶多会造成死循环,反而我们可以在得到溢出值后对该值进行准确的计算并判断该值是否溢出。
而有符号数回转之后返回的是一个不确定的值,我们无从判断该值是溢出后的值还是正常计算得到的值,反而无法对溢出现象进行判断更谈不上对该现象的纠正。
这样就会得到malloc((-n)*sizeof(char*);a[-n];等造成内存非法访问等比程序跑飞更加极度危险的情况。
所以按照你在4楼的论据得到的结论应该是“在数据类型允许的情况下,应该尽量使用无符号数字。”
同时在我3楼的帖子中给出的无符号数据在按位操作时更具有优越性。

以下是提问者的问题
以下是引用下凡小仙男在2020-3-18 18:11:50的发言:

#include<stdio.h>
int main(void)
{
    unsigned int a;
    while (a>0)
    a++;
    printf("unsign_int max %u\n",a-1);
    return 0;
}
输出的结果是正确答案
下面的代码却是一直在变化最大值一直递增不停又或者是什么都没有
#include<stdio.h>
int main(void)
{
    unsigned int a=1;
    while (a>0)
        a++;
    printf("unsigned_int max=%u\n",a-1);   
    return 0;
 
}

回答一,关于有符号类型数字溢出我已经在11楼回答了,有符号是回绕现象和无符号的益出不是一个现象
回答二,1.对的,无符号数有很多优点,就比如r版主说在效率上无符号要比有符号类型效率要高,2对于我说的避免使用无符号数这是我体现的主题,因为这是无符号类型的陷阱
,从我引用提问者的问题贴,可以看出这位初学者是掉入了unsigned类型无符号数字的陷阱中了,第一不用说是未初始化直接使用了,第二个正式我要说的陷阱,因为a会不停的加1,直到加到无符号类型的最大值,然后回绕成0,然后0-1变成一个负数,在无符号的陷阱中如果变成负号那就会成为一个很大的数,然后a变成0跳出循环,然后我就有了4楼的回帖,
回答三,是的,有符号如果益出将会出现不确定值,但是无符号没有溢出只会出现回绕,也就是它的余数会被保存下来,,而提问者出现的问题正是四楼我回答的答案,而且两个方面都包含了。一个是无符号数的溢出,另一个是无符号成为负数的种种情况
回答四 从你自己推论出内存访问之后的话我就不明白了
我一直说的是避免使用无符号数字,而不是使用无符号数字
至于优越性的问题,提问者也没提问优越性的问题,只提问为什么结果不一样,和为什么避免使用无符号数字
#22
lin51616782020-03-20 16:44
回复 21楼 叶纤
回答一,关于有符号类型数字溢出我已经在11楼回答了,有符号是回绕现象和无符号的益出不是一个现象

11楼的代码
比无符号数最大值大1 结果是 无符号数最小值
比有符号数最大值大1 结果是 有符号数最小值
为什么你会认为不是同一个现象??
#23
叶纤2020-03-20 17:38
回复 22楼 lin5161678
感谢帖主的提问,提问的非常好,虽然思想我是从老师那理解出来的,但是作为小白的我能代替我学习网站的老师回答您的问题,我是上辈子积了八辈子的好运
如果您是指数字溢出问题
八楼已经回答您了,这也是论坛老师的思维
整数溢出

如果我们尝试将值280分配给1个字节的有符号整数会怎样?此数字超出1字节有符号整数可以容纳的范围。数字280需要表示9位(加上1个符号位),但是在1字节有符号整数中,我们只有7位(加上1个符号位)可用。

当我们尝试存储超出类型范围的值时,会发生整数溢出(通常简称为溢出)。本质上,我们要存储的数字表示的位数比对象可用的位数更多。在这种情况下,数据丢失是因为对象没有足够的内存来存储所有内容。

在有符号整数的情况下,丢失哪些位的定义不明确,因此有符号整数溢出会导致未定义的行为。

警告

有符号整数溢出将导致不确定的行为。
通常,溢出会导致信息丢失,这几乎是不希望的。如果有任何怀疑的对象可能需要存储落在其范围之外的值,使用类型有一个更大的范围内!

如果您想知道回绕与溢出的区别,老师也有回答

整数的内部表示形式是实现定义的。大多数编译器使用二进制补码(在有关二进制转换的课程中介绍)。

2.
您可能会用完内存。

3.
溢出:添加一个数字,执行加法运算,所有不适合该类型的二进制数字都将被删除。
环绕:添加一个数字,如果该数字不适合您的类型,则其余数将被存储。

有符号数溢出(我不知道是否定义明确),无符号数显式环绕。

无符号类型的可表示值范围是0到2 N -1
(含)。无符号类型的算术以2 N为模执行。[注意:无符号算术不会溢出。有符号算术的溢出产生未定义的行为


#24
叶纤2020-03-20 18:09
说了这么多避免使用,4楼也提到了无符号数字在哪里用比较好,蓝版主已经提到了一条位操作的时候
不过四楼还另说里几条,感兴趣的话可以看看
1 那么使用无符号数字在哪里合理呢?
   
首先,在进行位操作时,首选无符号数字
其次,在某些情况下仍然不可避免地要使用无符号数,主要是那些与数组索引有关的数字。
另请注意,如果您是为嵌入式系统(例如Arduino)或其他处理器/内存受限的环境而开发的,出于性能方面的考虑,无符号数的使用更为常见并被接受(在某些情况下是不可避免的)。
#25
lin51616782020-03-20 18:32
以下是引用叶纤在2020-3-20 17:38:55的发言:

感谢帖主的提问,提问的非常好,虽然思想我是从老师那理解出来的,但是作为小白的我能代替我学习网站的老师回答您的问题,我是上辈子积了八辈子的好运
如果您是指数字溢出问题
八楼已经回答您了,这也是论坛老师的思维
整数溢出

如果我们尝试将值280分配给1个字节的有符号整数会怎样?此数字超出1字节有符号整数可以容纳的范围。数字280需要表示9位(加上1个符号位),但是在1字节有符号整数中,我们只有7位(加上1个符号位)可用。

当我们尝试存储超出类型范围的值时,会发生整数溢出(通常简称为溢出)。本质上,我们要存储的数字表示的位数比对象可用的位数更多。在这种情况下,数据丢失是因为对象没有足够的内存来存储所有内容。

在有符号整数的情况下,丢失哪些位的定义不明确,因此有符号整数溢出会导致未定义的行为。

警告

有符号整数溢出将导致不确定的行为。
通常,溢出会导致信息丢失,这几乎是不希望的。如果有任何怀疑的对象可能需要存储落在其范围之外的值,使用类型有一个更大的范围内!

如果您想知道回绕与溢出的区别,老师也有回答

整数的内部表示形式是实现定义的。大多数编译器使用二进制补码(在有关二进制转换的课程中介绍)。

2.
您可能会用完内存。

3.
溢出:添加一个数字,执行加法运算,所有不适合该类型的二进制数字都将被删除。
环绕:添加一个数字,如果该数字不适合您的类型,则其余数将被存储。

有符号数溢出(我不知道是否定义明确),无符号数显式环绕。

无符号类型的可表示值范围是0到2 N -1
(含)。无符号类型的算术以2 N为模执行。[注意:无符号算术不会溢出。有符号算术的溢出产生未定义的行为

这段话可以清楚的看到你对这里的内容理解存在偏差

对于 整数溢出 这个情况
首选建议是选择更大类型
没有更大类型使用无符号
有符号类型不可取
所以我一直强调 整数溢出 无法作为避免无符号类型的理由

用完内存的说法是无稽之谈 和有符号/无符号毫无关系

计算结果超出取值范围就是溢出
环绕是溢出之后的现象
你对这两个词的意思的理解非常奇怪

然后不管是溢出还是环绕 都是在描述无符号类型的优点/有符号类型的缺点
而你却企图用无符号类型的有点证明要避免使用无符号类型
这是我想不通的
#26
叶纤2020-03-20 18:52
以下是引用lin5161678在2020-3-20 18:32:29的发言:


这段话可以清楚的看到你对这里的内容理解存在偏差

对于 整数溢出 这个情况
首选建议是选择更大类型
没有更大类型使用无符号
有符号类型不可取
所以我一直强调 整数溢出 无法作为避免无符号类型的理由

用完内存的说法是无稽之谈 和有符号/无符号毫无关系

计算结果超出取值范围就是溢出
环绕是溢出之后的现象
你对这两个词的意思的理解非常奇怪

然后不管是溢出还是环绕 都是在描述无符号类型的优点/有符号类型的缺点
而你却企图用无符号类型的有点证明要避免使用无符号类型
这是我想不通的

有符号类型数字和无符号类型数字,是我理解有问题吗
#27
d7se1232020-03-20 19:50
回复 14楼 return_0
我的求助帖子要是有这么多人就好了 吵我不怕啊
#28
自学的数学2020-03-20 19:56
你想你的帖子有这么多的跟帖,那你就得提出有分量的问题,或者有分量的心得。不然就没戏。
#29
lin51616782020-03-20 20:27
咬文嚼字??
请讲一下 避免使用无符号整数 和 避免使用无符号类型有什么区别
这不是一回事?
#30
自学的数学2020-03-20 20:44
有符号就是最高位是符号位,其余的位是数据位。无符号就是所有位都是数据位。比如char类型是8位的,其中最高位是符号位1=00000001b,-1=11111111b(这里涉及到了补码),它能表示的范围是-128~+127。unsigned char 是无符号字符型也是8位,它没有符号位,最高位是数据位,比如1=00000001b,255=11111111b,它的范围是0~255。
#31
rjsp2020-03-20 21:19
回复 30楼 自学的数学
char 有 CHAR_BIT 位,只是C语言规定 CHAR_BIT 必须大于等于 8。
现在9bits的不常见,但10bits、12bits、16bits的还挺多的。

signed char 是具符号的,unsigned char 是无符号。
但 char 是否具符号由实现定义,比如 gcc 中char默认就是无符号的。
C语言规定 char、signed char、unsigned char 是三种不同的类型。
#32
forever742020-03-20 21:35
嗯,我来敲敲黑板。

上方讨论的是溢出,就是数据类型长度有限,当数值大到一定程度造成位数不够用的现象。
首先引经据典,C标准说过,(下面的“标准”是个名词,指C语言的规范,请大家脑补,给这个词拟人化一下)
    无符号整数的溢出(请注意,首先是溢出)结果是确定的,我就不多说了;
    有符号整数的溢出是未定义行为。这个就是标准自己的局限了(某种意义上)或者说正是标准不受局限的地方(在某'种意义上)。

那么,标准为什么这样说呢?因为标准的野心是想要C语言能运行在一切硬件平台上。
某些设计思路比较清奇的硬件平台可能会使用比较清奇的方式表达有符号整数溢出这件事,标准说我容忍你的清奇,我不表态(这就是未定义行为这个词在这个知识点上的含义)。

但是(某位腕儿说过,其实但是之前都是废话),
由于小F我见识有限,只见过不那么清奇的硬件,所以一般情况下有符号整数的溢出结果也是可预期的
它和无符号整数一样,最大值再加一会变成最小值。
也就是说signed char类型最大表示127,再走一步就变成-128。
一般的signed short如果是16位,它表示的最大就是32767,再前进就是-32768。
也就是说,在眼前常见的硬件上,占用n位二进制位的有符号整数,它能表示的最大值是2的n-1次方减1,再加1造成溢出,会绕回到负的2的n-1次方。

所以,溢出是一个定义性术语,绕回是一个描述性词汇,它俩不会对撞什么的。
那么最后,
需要使用有符号整数的时候使用有符号整数
需要使用无符号整数的时候使用无符号整数
无所谓的时候无所谓
#33
forever742020-03-20 21:51
标准之所以讲话很不爽利是因为它端着架子,很怕被打脸,因此凡是有可能有例外的地方它都不说个痛快话儿。
所以我们看到未定义行为、未指明行为 和 实现定义行为,要辩证地理解。
#34
rjsp2020-03-20 23:38
以下是引用forever74在2020-3-20 21:35:30的发言:
一般情况下有符号整数的溢出结果也是可预期的

并不这样,当然或许在汇编层面你说得对。
但既然C/C++规定了 具符号的溢出属于未定义行为,那C编译器就可以据此进行代码优化。
此贴是由 https://bbs.bccn.net/thread-500418-1-1.html 引发的,就拿他的代码举例,如果其代码中将无符号类型换成具符号类型的话,那么 clang 等编译器就会将 while(a>0) 替换成死循环。
clang为此作过解释,大体意思是:
既然具符号类型一开始大于0,那么自增后只能更大于零,不可能会变小;
如果说溢出,因为具符号类型的溢出是未定义行为,所以死循环也是符合标准规定的行为之一。
#35
forever742020-03-20 23:46
我和标准风格不同。
我预防打脸的方式是“见识有限”和“一般情况下”。
而且“一般”也不是学术意义,而是某种口语环境下表达非特定的常见的意思。
#36
d7se1232020-03-21 13:38
回复 27楼 d7se123
谢谢老弟了  
#37
lidepeng19952020-03-21 14:07
回复 楼主 叶纤
我听说无符号类型效率好就用无符号进行加减运算,每次小的数减去大的数都不是正确结果,原来是无符号的陷阱啊
了解到了,溢出的话直接换更大的类型,楼主说的对在加减运算中确实容易出现不好的结果
#38
lidepeng19952020-03-21 14:21
那个争论的帖子我也看了,2楼应该关注的结果,说应该换更大的类型,
楼主回复的应该是那个帖子对于
避免使用无符号类型,因为使用无符号进行运算有可能会出现环绕和益处,就比如你第二个代码,如果益处就会进入环绕状态,也就是说最后是1-1=0,0不大于0为faulse,此时终止循环,楼主又说了把a定义为0,那就直接faulse,然后直接a-1为复数,此时进行了环绕为最大值

产生的提问
以下是引用下凡小仙男在2020-3-18 22:06:59的发言:

就是unsigned 少用吗 我今天看视频学到了这个就用了下哈哈哈
然后就有了楼主的回答吧?
2楼的回答对于溢出的话其实也没错,但是对于加减运算上出现负数的情况2楼应该也是清楚的只是没说出来
1