unsigned 关键字可以以非显而易见的方式使用吗?

发布于 2024-12-09 12:12:31 字数 311 浏览 0 评论 0原文

每次我使用 unsigned 关键字时,它都会出现在 int 或其他内置类型之前。 我想知道是否还有其他方式可以使用unsigned

  • 用户定义的类型(类/结构)可以使用unsigned关键字吗?
  • 模板可以特殊使用unsigned吗?

如果不是,为什么它有自己的关键字?为什么unsigned int 不是uint

Every time I've used the unsigned keyword it has been before int, or another built-in type. I was wondering if there were any other ways unsigned could be used.

  • Can user defined types (classes/structs) use the unsigned keyword?
  • Can templates make special use with unsigned?

If not, why does it have its own keyword? Why is unsigned int not uint?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

星星的轨迹 2024-12-16 12:12:31

主要问题已经回答了好几次:unsigned 关键字只能用作整型的类型说明符。

至于为什么 unsigned 是一个单独的关键字,而不是一个 uint 关键字,其原因是历史性的。

C 的最早版本(K&R 之前)只有四种基本类型:

  • char(8 位,有符号,2 的补码)
  • int(16 位,有符号, 2 的补码)
  • float(32 位)
  • double(64 位,与 float 范围相同,但精度更高)

注意缺少什么:否signedunsigned 关键字,无 shortlonglong double;所有这些都是后来添加的。 (需要无符号算术的程序员通常使用指针,它们可以与 int 自由互换。)

每个基本类型都有一个作为单个关键字的名称,这使得语法简单明了。

当后来添加其他类型时,将 unsignedshortlong 等说明符添加到现有类型名称中而不是引入新的类型是有意义的关键字(可能会破坏现有代码)。当 ANSI C 委员会于 1989 年对该语言进行标准化时,他们必须根据现有的不太正式的定义制定一个连贯的结构,同时与现有的实现保持一致。结果就是我们现在所看到的,其中 int long unsigned long 是一个有效的类型名称(更常见地写为 unsigned long long)。

如果现在从头开始设计该语言,我怀疑会采取不同的方法。也许每个基本类型都有一个关键字(例如,C# 采用的方法),或者基本类型名称可能会使用一些更连贯的方案,而不是一堆关键字(例如,int:2 表示 2 字节整数,unsigned:4 表示 4 字节无符号整数)。但 C 和 C++ 都坚持当前的方法。

参考:http://cm.bell-labs.com/cm /cs/who/dmr/cman.pdf

The main question has been answered several times: the unsigned keyword can only be used as a type-specifier for an integral type.

As for why unsigned is a separate keyword, rather than having, say, a uint keyword, the reasons for that are historical.

The earliest versions of C (pre-K&R) had only four fundamental types:

  • char (8 bits, signed, 2's-complement)
  • int (16 bits, signed, 2's-complement)
  • float (32 bits)
  • double (64 bits, same range as float but greater precision)

Note what's missing: no signed or unsigned keywords, no short, long, or long double; all those were added later. (Programmers who needed unsigned arithmetic commonly used pointers, which were freely interchangeable with int.)

Each fundamental type had a name that was a single keyword, which made the grammar straightforward.

When other types were added later, it made sense to add specifiers like unsigned, short, and long to the existing type names rather than introducing new keywords (which might have broken existing code). When the ANSI C committee standardized the language in 1989, they had to make a coherent structure out of the existing not-quite-formal definitions while remaining consistent with existing implementations. The result is what we have now, where int long unsigned long is a valid type name (more commonly written as unsigned long long).

If the language were being designed from scratch now, I suspect that a different approach would have been taken. Perhaps there would be a single keyword for each fundamental type (that's the approach taken by C#, for example), or perhaps the fundamental type names would use some more coherent scheme rather than a jumble of keywords (say, int:2 for a 2-byte integer, unsigned:4 for a 4-byte unsigned integer). But both C and C++ are stuck with the current approach.

Reference: http://cm.bell-labs.com/cm/cs/who/dmr/cman.pdf

jJeQQOZ5 2024-12-16 12:12:31

不,它不能与类或结构一起使用,因为它们不是整型。模板所能做的就是使 int 成为无符号的。我认为它被选为一个单独的关键字,因为它可以应用于任何整数类型(char、int、long、long long),从而用一个关键字实现需要四个关键字的效果。它的含义也很明显,而 uint、uchar 等则不一定。请记住,它也可以在没有限定符的情况下单独使用,在这种情况下,假定为 unsigned int。

No, it can't be used with classes or structs since they're not integral types. All a template can do with it is make an int unsigned. I think it was chosen as a separate keyword since it can be applied to any integer type (char, int, long, long long), thereby achieving with one keyword what would have required four more. Its meaning is also immediately evident, whereas uint, uchar, etc. aren't necessarily. And keep in mind it can also be used by itself without a qualifier, in which case unsigned int is assumed.

魄砕の薆 2024-12-16 12:12:31

unsigned 是一个关键字。 unsigned 只能用于整型 类型。 unsignedsigned 被视为类型说明符,简单类型说明符。所以他们指定类型要么是有符号的,要么是无符号的。

您可以通过 typedef 将单词 unsigned int 转换为 uint,但是当 int 为类型和 unsigned 本身就是类型说明符。

与普遍看法相反,您可以单独使用 unsigned,如 C++ ISO 标准第 7.1.5.2 节表 7 所示:

在此处输入图像描述

您可以使用多个类型说明符(仅在允许时),并且可以按任何顺序与 decl 说明符自由混合。

您也可以这样做:

int unsigned i;

这是有效的 C++。

unsigned is a keyword. unsigned can only be used on integral types. unsigned or signed is considered a type-specifier, a simple type-specifier. So they specify that the type will either by signed or unsigned.

You can typedef the words unsigned int to uint but then that would make it look like a type when int is the type and unsigned is the type-specifier per se.

You can use unsigned by itself, contrary to popular belief, as evidenced in the C++ ISO Standard Section 7.1.5.2 Table 7:

enter image description here

You can use multiple type-specifiers (when only allowed) and can be freely mixed with decl-specifiers in any order.

You can also do this:

int unsigned i;

and this is valid C++.

此生挚爱伱 2024-12-16 12:12:31

No unsigned 本身并不是一个真正的关键字;它只是 int、short、char 的修饰符。

unsigned 关键字可以用作 intshortcharlong。它也可以单独用作类型名称; unsigned 表示unsigned int

这样做是为了避免使用三个额外的关键字 uint ushort uchar 并且因为在 C 和 Unix 所针对的所有早期计算机上有符号/无符号可能没有不同。

No unsigned isn't really a keyword on its own; it's only a modifier for int, short, char.

The unsigned keyword can be used as a modifier for int, short, char, or long. It can also be used on its own as a type name; unsigned means unsigned int.

It was done that way to avoid having three extra keywords uint ushort uchar and because signed/unsigned might not have been different on all the early computers C and Unix were aimed at.

離人涙 2024-12-16 12:12:31

关键字signed、unsigned、short 和long 是类型修饰符/说明符,当这些类型修饰符/说明符之一单独使用时,假定数据类型为int。

因此,signed 和 unsigned 也可以用作独立类型说明符,分别与signed int 和unsigned int 含义相同。以下两个声明是等效的:

unsigned abc;

unsigned int abc;

The keywords signed, unsigned, short, and long are type modifiers/specifier and When one of these type modifiers/specifier is used by itself, a data type of int is assumed.

So signed and unsigned may also be used as standalone type specifiers, meaning the same as signed int and unsigned int respectively. The following two declarations are equivalent:

unsigned abc;

unsigned int abc;
薄荷梦 2024-12-16 12:12:31

还有无符号字符。有些编译器,例如 GNU 的 g++,允许您只输入 unsigned 并假设您的意思是 unsigned int。例如以下代码是等效的

unsigned int x;

unsigned x;

There is also unsigned char. Some compilers, such as GNU's g++, let you just put unsigned and assume you mean unsigned int. For example the following code is equivalent

unsigned int x;

unsigned x;
长伴 2024-12-16 12:12:31

SerenityOS 有一篇博客文章介绍了如何将 unsigned 重新定义为 signed,以在标头中实现 size_thttps://awesomekling.github.io/How-SerenityOS-declares-ssize_t/

#define unsigned signed
typedef __SIZE_TYPE__ ssize_t;
#undef unsigned

这可能看起来很黑客,但如果你只支持 GCC/Clang),它们是唯一实现 的编译器__SIZE_TYPE__,并且需要在没有任何系统库的情况下独立编译(就像内核的情况一样),那么这种定义 ssize_t 的方法是可靠的,而且更可靠比令人讨厌的#ifdef迷宫更容易维护。

为了参考和好奇,让我们研究一下 ssize_t 通常如何/何时在我的 x86_64 Linux Mint 机器上定义。

ssize_t 可以在许多不同的地方按需定义,例如定义 __USE_XOPEN2K8 时的 (旧旧遗留)东西)。但是,ssize_t 定义的主要位置是 /usr/include/x86_64-linux-gnu/sys/types.h 的第 107-110 行,通常将其带入通过包含。以下是第 107-110 行:

#ifndef __ssize_t_defined
typedef __ssize_t ssize_t;
# define __ssize_t_defined
#endif

__ssize_t 来自 /usr/include/x86_64-linux-gnu/bits/types.h 的第 193 行:

__STD_TYPE __SSIZE_T_TYPE __ssize_t; /* Type of a byte count, or error.  */

__SSIZE_T_TYPE 来自 /usr/include/x86_64-linux-gnu/bits/typesizes.h 第 73 行:

#define __SSIZE_T_TYPE      __SWORD_TYPE

__SWORD_TYPE 来自 /usr/include/x86_64-linux-gnu/bits/types.h 的第 115-114 行:

#if __WORDSIZE == 32
# define __SQUAD_TYPE       __int64_t
# define __UQUAD_TYPE       __uint64_t
# define __SWORD_TYPE       int
# define __UWORD_TYPE       unsigned int
# define __SLONG32_TYPE     long int
# define __ULONG32_TYPE     unsigned long int
# define __S64_TYPE     __int64_t
# define __U64_TYPE     __uint64_t
/* We want __extension__ before typedef's that use nonstandard base types
   such as `long long' in C89 mode.  */
# define __STD_TYPE     __extension__ typedef
#elif __WORDSIZE == 64
# define __SQUAD_TYPE       long int
# define __UQUAD_TYPE       unsigned long int
# define __SWORD_TYPE       long int
# define __UWORD_TYPE       unsigned long int
# define __SLONG32_TYPE     int
# define __ULONG32_TYPE     unsigned int
# define __S64_TYPE     long int
# define __U64_TYPE     unsigned long int
/* No need to mark the typedef with __extension__.   */
# define __STD_TYPE     typedef
#else
# error
#endif

__WORDSIZE 来自从 /usr/include/x86_64-linux-gnu/bits/wordsize.h 第 3-9 行

#if defined __x86_64__ && !defined __ILP32__
# define __WORDSIZE 64
#else
# define __WORDSIZE 32
#define __WORDSIZE32_SIZE_ULONG     0
#define __WORDSIZE32_PTRDIFF_LONG   0
#endif

__x86_64____ILP32__ 都是编译器提供的预定义宏,并从根据源代码构建编译器的 makefile 中烘焙到编译器的二进制文件中。

/usr/include/x86_64-linux-gnu 下的所有文件都特定于我的计算机的本机 x86_64 架构,并且 x86_64 专门是 x86 ABI 的 SysV 64 位扩展。

检查 定义的 __x86_64__ &&仅当将 x86_64 编译器与 -m32-mx32 一起使用以强制其分别生成 32 位或 x32-abi 代码时,!define __ILP32__ 才为 false 。然而,编译 32 位二进制文​​件的更常见方法是安装相应的 GCC 多架构编译器。

为了进行比较,musl libc 采用更短的路径,仅在特定于体系结构的标头基础上将所有类型定义为其各自的固定宽度整数。对于 x86_64: https://git.musl-libc.org/cgit/musl/tree/arch/x86_64/bits/stdint.h?id=3f08154ac494f4739afbc7451f317b2ef1bffbd3

SerenityOS has a blog post about how they redefine unsigned to signed briefly to implement size_t in their headers: https://awesomekling.github.io/How-SerenityOS-declares-ssize_t/

#define unsigned signed
typedef __SIZE_TYPE__ ssize_t;
#undef unsigned

This may look hackish but if you are only supporting GCC/Clang), which are the only compilers to implement __SIZE_TYPE__, and need to compile freestanding without any system libraries (as is the case for kernels), then this approach to defining ssize_t is solid, reliable, and far more maintainable than a nasty maze of #ifdefs.

For reference and curiosity, let's investigate how/when ssize_t is normally defined on my x86_64 Linux Mint machine.

ssize_t can be defined on-demand in a number of different places such as <stdio.h> when __USE_XOPEN2K8 is defined (old old legacy stuff). BUT, the primary place ssize_t is defined is on lines 107-110 of /usr/include/x86_64-linux-gnu/sys/types.h, which is typically brought in by including <unistd.h>. Here are lines 107-110:

#ifndef __ssize_t_defined
typedef __ssize_t ssize_t;
# define __ssize_t_defined
#endif

The __ssize_t comes from line 193 of /usr/include/x86_64-linux-gnu/bits/types.h:

__STD_TYPE __SSIZE_T_TYPE __ssize_t; /* Type of a byte count, or error.  */

The __SSIZE_T_TYPE comes from line 73 of /usr/include/x86_64-linux-gnu/bits/typesizes.h:

#define __SSIZE_T_TYPE      __SWORD_TYPE

The __SWORD_TYPE comes from lines 115-114 of /usr/include/x86_64-linux-gnu/bits/types.h:

#if __WORDSIZE == 32
# define __SQUAD_TYPE       __int64_t
# define __UQUAD_TYPE       __uint64_t
# define __SWORD_TYPE       int
# define __UWORD_TYPE       unsigned int
# define __SLONG32_TYPE     long int
# define __ULONG32_TYPE     unsigned long int
# define __S64_TYPE     __int64_t
# define __U64_TYPE     __uint64_t
/* We want __extension__ before typedef's that use nonstandard base types
   such as `long long' in C89 mode.  */
# define __STD_TYPE     __extension__ typedef
#elif __WORDSIZE == 64
# define __SQUAD_TYPE       long int
# define __UQUAD_TYPE       unsigned long int
# define __SWORD_TYPE       long int
# define __UWORD_TYPE       unsigned long int
# define __SLONG32_TYPE     int
# define __ULONG32_TYPE     unsigned int
# define __S64_TYPE     long int
# define __U64_TYPE     unsigned long int
/* No need to mark the typedef with __extension__.   */
# define __STD_TYPE     typedef
#else
# error
#endif

The __WORDSIZE comes from lines 3-9 of /usr/include/x86_64-linux-gnu/bits/wordsize.h:

#if defined __x86_64__ && !defined __ILP32__
# define __WORDSIZE 64
#else
# define __WORDSIZE 32
#define __WORDSIZE32_SIZE_ULONG     0
#define __WORDSIZE32_PTRDIFF_LONG   0
#endif

The __x86_64__ and __ILP32__ are both predefined macros provided by the compiler and baked into the compiler's binary from the makefile that built it from its source code.

All files under /usr/include/x86_64-linux-gnu are specific to the native x86_64 architecture of my computer, and x86_64 is exclusively the SysV 64-bit extension of the x86 ABI.

The check for defined __x86_64__ && !defined __ILP32__ is only false if using the x86_64 compiler with -m32 or -mx32 to force it to generate 32-bit or x32-abi code, respectively. However, the more common approach to compile 32-bit binaries is to install the respective GCC multiarch compiler.

For comparison, musl libc takes a much shorter rout and just defines all the types to their respective fixed-width integers on an architecture-specific-header basis. For x86_64: https://git.musl-libc.org/cgit/musl/tree/arch/x86_64/bits/stdint.h?id=3f08154ac494f4739afbc7451f317b2ef1bffbd3

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文