- 浏览: 543731 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (231)
- 一个操作系统的实现 (20)
- 汇编(NASM) (12)
- Linux编程 (11)
- 项目管理 (4)
- 计算机网络 (8)
- 设计模式(抽象&封装) (17)
- 数据结构和算法 (32)
- java基础 (6)
- UML细节 (2)
- C/C++ (31)
- Windows (2)
- 乱七八糟 (13)
- MyLaB (6)
- 系统程序员-成长计划 (8)
- POJ部分题目 (10)
- 数学 (6)
- 分布式 & 云计算 (2)
- python (13)
- 面试 (1)
- 链接、装载与库 (11)
- java并行编程 (3)
- 数据库 (0)
- 体系结构 (3)
- C++ template / STL (4)
- Linux环境和脚本 (6)
最新评论
-
chuanwang66:
默默水塘 写道typedef void(*Fun)(void) ...
C++虚函数表(转) -
默默水塘:
typedef void(*Fun)(void);
C++虚函数表(转) -
lishaoqingmn:
写的很好,例子简单明了,将观察者模式都表达了出来。
这里是ja ...
观察者模式——Observer
Abstract
The article will help the readers understand what size_t and ptrdiff_t types are, what they are used for and when they must be used. The article will be interesting for those developers who begin creation of 64-bit applications where use of size_t and ptrdiff_t types provides high performance, possibility to operate large data sizes and portability between different platforms.
Introduction
Before we begin I would like to notice that the definitions and recommendations given in the article refer to the most popular architectures for the moment (IA-32, Intel 64, IA-64) and may not fully apply to some exotic architectures.
The types size_t and ptrdiff_t were created to perform correct address arithmetic. It had been assumed for a long time that the size of int coincides with the size of a computer word (microprocessor's capacity) and it can be used as indexes to store sizes of objects or pointers. Correspondingly, address arithmetic was built with the use of int and unsigned types as well. int type is used in most training materials on programming in C and C++ in the loops' bodies and as indexes. The following example is nearly a canon:
for (int i = 0; i < n; i++) a[i] = 0;
As microprocessors developed over time and their capacity increased, it became irrational to further increase int type's sizes. There are a lot of reasons for that: economy of memory used, maximum portability etc. As a result, several data model appeared declaring the relations of C/C++ base types. Table N1 shows the main data models and lists the most popular systems using them.
Table N1. Data models
As you can see from the table, it is not so easy to choose a variable's type to store a pointer or an object's size. To find the smartest solution of this problem size _t and ptrdiff_t types were created. They are guaranteed to be used for address arithmetic. And now the following code must become a canon:
for(ptrdiff_t i =0; i < n; i++) a[i]=0;
It is this code that can provide safety, portability and good performance. The rest of the article explains why.
size_t type
size_t type is a base unsigned integer type of C/C++ language. It is the type of the result returned by sizeof operator. The type's size is chosen so that it could store the maximum size of a theoretically possible array of any type. On a 32-bit system size_t will take 32 bits, on a 64-bit one 64 bits. In other words, a variable of size_t type can safely store a pointer. The exception is pointers to class functions but this is a special case. Although size_t can store a pointer, it is better to use another unsinged integer type uintptr_t for that purpose (its name reflects its capability). The types size_t and uintptr_t are synonyms. size_t type is usually used for loop counters, array indexing and address arithmetic.
The maximum possible value of size_t type is constant SIZE_MAX.
ptrdiff_t type
ptrdiff_t type is a base signed integer type of C/C++ language. The type's size is chosen so that it could store the maximum size of a theoretically possible array of any type. On a 32-bit system ptrdiff_t will take 32 bits, on a 64-bit one 64 bits. Like in size_t, ptrdiff_t can safely store a pointer except for a pointer to a class function. Also, ptrdiff_t is the type of the result of an expression where one pointer is subtracted from the other (ptr1-ptr2). ptrdiff_t type is usually used for loop counters, array indexing, size storage and address arithmetic. ptrdiff_t type has its synonym intptr_t whose name indicates more clearly that it can store a pointer.
Portability of size_t and ptrdiff_t
The types size_t and ptrdiff_t enable you to write well-portable code. The code created with the use of size_t and ptrdiff_t types is easy-portable. The size of size_t and ptrdiff_t always coincide with the pointer's size. Because of this, it is these types that should be used as indexes for large arrays, for storage of pointers and pointer arithmetic.
Linux-application developers often use long type for these purposes. Within the framework of 32-bit and 64-bit data models accepted in Linux, this really works. long type's size coincides with the pointer's size. But this code is incompatible with Windows data model and, consequently, you cannot consider it easy-portable. A more correct solution is to use types size_t and ptrdiff_t.
As an alternative to size_t and ptrdiff_t, Windows-developers can use types DWORD_PTR, SIZE_T, SSIZE_T etc. But still it is desirable to confine to size_t and ptrdiff_t types.
Safety of ptrdiff_t and size_t types in address arithmetic
Address arithmetic issues have been occurring very frequently since the beginning of adaptation of 64-bit systems. Most problems of porting 32-bit applications to 64-bit systems relate to the use of such types as int and long which are unsuitable for working with pointers and type arrays. The problems of porting applications to 64-bit systems are not limited by this, but most errors relate to address arithmetic and operation with indexes.
Here is a simplest example:
size_t n =...; for(unsigned i =0; i < n; i++) a[i]=0;
If we deal with the array consisting of more than UINT_MAX items, this code is incorrect. It is not easy to detect an error and predict the behavior of this code. The debug-version will hung but hardly will anyone process gigabytes of data in the debug-version. And the release-version, depending on the optimization settings and code's peculiarities, can either hung or suddenly fill all the array cells correctly producing thus an illusion of correct operation. As a result, there appear floating errors in the program occurring and vanishing with a subtlest change of the code. To learn more about such phantom errors and their dangerous consequences see the article "A 64-bit horse that can count" [1].
Another example of one more "sleeping" error which occurs at a particular combination of the input data (values of A and B variable):
int A =-2; unsigned B =1; int array[5]={1,2,3,4,5}; int*ptr = array +3; ptr = ptr +(A + B);//Error printf("%i\n",*ptr);
This code will be correctly performed in the 32-bit version and print number "3". After compilation in 64-bit mode there will be a fail when executing the code. Let's examine the sequence of code execution and the cause of the error:
- A variable of int type is cast into unsigned type;
- A and B are summed. As a result, we get 0xFFFFFFFF value of unsigned type;
- "ptr + 0xFFFFFFFFu" expression is calculated. The result depends on the pointer's size on the current platform. In the 32-bit program, the expression will be equal to "ptr - 1" and we will successfully print number 3. In the 64-bit program, 0xFFFFFFFFu value will be added to the pointer and as a result, the pointer will be far beyond the array's limits.
注: 哪个能表示更大的数就转为哪个类型。例如short+long,就要转为long.
在32位机上, unsigned int 最大可表示2^32 - 1; int最大可表示2^31-1, 这样int就转为了unsigned int
Such errors can be easily avoided by using size_t or ptrdiff_t types. In the first case, if the type of "i" variable is size_t, there will be no infinite loop. In the second case, if we use size_t or ptrdiff_t types for "A" and "B" variable, we will correctly print number "3".
Let's formulate a guideline: wherever you deal with pointers or arrays you should use size_t and ptrdiff_t types.
To learn more about the errors you can avoid by using size_t and ptrdiff_t types, see the following articles:
- 20 issues of porting C++ code on the 64-bit platform [2];
- Safety of 64-bit code [3];
- Traps detection during migration of C and C++ code to 64-bit Windows [4].
Performance of code using ptrdiff_t and size_t
Besides code safety, the use of ptrdiff_t and size_t types in address arithmetic can give you an additional gain of performance. For example, using int type as an index, the former's capacity being different from that of the pointer, will lead to that the binary code will contain additional data conversion commands. We speak about 64-bit code where pointers' size is 64 bits and int type's size remains 32 bits.
It is a difficult task to give a brief example of size_t type's advantage over unsigned type. To be objective we should use the compiler's optimizing abilities. And the two variants of the optimized code frequently become too different to show this very difference. We managed to create something like a simple example only with a sixth try. And still the example is not ideal because it demonstrates not those unnecessary data type conversions we spoke above, but that the compiler can build a more efficient code when using size_t type. Let's consider a program code arranging an array's items in the inverse order:
unsigned arraySize; ... for(unsigned i =0; i < arraySize /2; i++){ float value = array[i]; array[i]= array[arraySize - i -1]; array[arraySize - i -1]= value; }
In the example, "arraySize" and "i" variables have unsigned type. This type can be easily replaced with size_t type, and now compare a small fragment of assembler code shown on Figure 1.
Figure N1.Comparison of 64-bit assembler code when using unsigned and size_t types
The compiler managed to build a more laconic code when using 64-bit registers. I am not affirming that the code created with the use of unsigned type will operate slower than the code using size_t. It is a very difficult task to compare speeds of code execution on modern processors. But from the example you can see that when the compiler operates arrays using 64-bit types it can build a shorter and faster code.
Proceeding from my own experience I can say that reasonable replacement of int and unsigned types with ptrdiff_t and size_t can give you an additional performance gain up to 10% on a 64-bit system. You can see an example of speed increase when using ptrdiff_t and size_t types in the fourth section of the article "Development of Resource-intensive Applications in Visual C++" [5].
Code refactoring with the purpose of moving to ptrdiff_t and size_t
As the reader can see, using ptrdiff_t and size_t types gives some advantages for 64-bit programs. However, it is not a good way out to replace all unsigned types with size_t ones. Firstly, it does not guarantee correct operation of a program on a 64-bit system. Secondly, it is most likely that due to this replacement, new errors will appear data format compatibility will be violated and so on. You should not forget that after this replacement the memory size needed for the program will greatly increase as well. And increase of the necessary memory size will slow down the application's work for cache will store fewer objects being dealt with.
Consequently, introduction of ptrdiff_t and size_t types into old code is a task of gradual refactoring demanding a great amount of time. In fact, you should look through the whole code and make the necessary alterations. Actually, this approach is too expensive and inefficient. There are two possible variants:
- To use specialized tools like Viva64 included into PVS-Studio. Viva64 is a static code analyzer detecting sections where it is reasonable to replace data types for the program to become correct and work efficiently on 64-bit systems. To learn more, see "PVS-Studio Tutorial" [6].
- If you do not plan to adapt a 32-bit program for 64-bit systems, there is no sense in data types' refactoring. A 32-bit program will not benefit in any way from using ptrdiff_t and size_t types.
References
- Andrey Karpov. A 64-bit horse that can count. http://www.viva64.com/en/a/0043/
- Andrey Karpov, Evgeniy Ryzhkov. 20 issues of porting C++ code on the 64-bit platform.http://www.viva64.com/en/a/0004/
- Andrey Karpov. Safety of 64-bit code. http://www.viva64.com/en/a/0046/
- Andrey Karpov, Evgeniy Ryzhkov. Traps detection during migration of C and C++ code to 64-bit Windows. http://www.viva64.com/en/a/0012/
- Andrey Karpov, Evgeniy Ryzhkov. Development of Resource-intensive Applications in Visual C++.http://www.viva64.com/en/a/0018/
- Evgeniy Ryzhkov. PVS-Studio Tutorial. http://www.viva64.com/en/d/0011/
本文转自:
http://www.viva64.com/en/a/0050/
发表评论
-
C++引用计数
2013-12-29 14:48 1167主要参考《提高C++性能的编程技术》第12章 引用计数 ... -
string.h(二)C的字符串分隔函数strtok()
2013-12-19 13:25 1509char *strtok(char *str1, c ... -
new, operator new 和 placement new
2013-12-12 17:08 1078一、new 和 delete 的过程: 在进行一切讲解之 ... -
C/C++柔性数组 char[0]
2013-11-08 15:57 1012在标准C和C++中0长数组 ... -
填充与对齐——指定变量的地址偏移【转】
2013-11-08 14:53 860转自 http://hi.baidu.com/bai_ye ... -
数组和指针并不同(总结《C专家编程chap4》)
2013-10-06 14:55 941左值(地址)和右值(地址的内容): 1. 使用数组 ... -
细说C++全局变量、局部变量和静态局部变量【转】
2013-08-15 17:17 864转自http://see.xidian.ed ... -
strcpy和strncpy用法和区别(转)
2013-07-31 17:37 31strcpy和strncpy用法和区别 转自:http ... -
stl iterator&const_iterator
2013-07-11 09:38 14iterator和const_iterator访问conta ... -
C++类对象创建过程揭密(转载)
2013-05-24 16:56 929转载:http://blog.csdn.net ... -
C调用汇编
2013-03-27 20:28 1460一、汇编语言的两种语法格式 Intel格式: 大 ... -
C++四个类型转换关键字const_cast, static_cast, dynamic_cast, reinterpret_cast
2013-02-27 10:13 2537C风格的强制类型转换( ... -
C/C++中内存模型
2012-12-30 11:07 1634一直以来,C++ ... -
map使用
2012-12-29 15:21 35需要#include <map> 定义map& ... -
C++调用C
2012-11-29 22:42 1279一般地,C++调用C有两种形式(网上资料一般没讲全): ... -
关于计算精度
2012-11-22 14:53 876本文是Sam我总结的,在C++学习过程中常见的一些涉及 ... -
C++随机数
2012-11-07 19:41 915C++随机数 #include<iostream ... -
C文件操作(一)
2012-11-07 19:11 1612C文件操作(流式文件 &a ... -
C++中的内联函数inline
2012-07-13 19:16 1074在c++中,为了解决一些频繁调用的小函数大量消耗栈空间或者 ... -
模板和泛型编程???从P269继续
2012-04-22 09:58 843参考书 <C++ By Dissection> ...
相关推荐
1. `%zu`、`%zi`、`%zU`等:用于打印`size_t`、`ptrdiff_t`等类型。 2. `%hhx`、`%hhd`等:用于打印`int8_t`、`uint8_t`等类型的十六进制和十进制表示。 3. `%jx`、`%jd`等:用于打印`intmax_t`、`uintmax_t`等类型...
sbrk() 函数的原型是 `void *sbrk(ptrdiff_t increment);`,它将数据段的大小增加 `increment` 字节。sbrk() 函数不是系统调用,而是 C 库函数,它调用 brk() 函数来实现虚拟内存到内存的映射。 malloc() 函数是 C ...
C语言中的指针是程序设计中的...通过使用`size_t`、`ptrdiff_t`、`intptr_t`和`uintptr_t`,开发者可以编写出更加灵活和适应性强的程序,这些程序不仅能在当前平台良好运行,也能在未来的硬件和操作系统上保持兼容性。
正确使用`size_t`、`ptrdiff_t`、`intptr_t`和`uintptr_t`等类型可以帮助确保代码在不同平台上的一致性和兼容性。在编程时,应该根据具体情况选择合适的类型,以充分利用C语言的灵活性并避免潜在的移植问题。
1. **基本数据类型和宏定义**:在`bits/types.h`等头文件中,glibc定义了与平台相关的数据类型,如size_t、ptrdiff_t等,并提供了各种宏来处理类型转换和边界检查。 2. **内存管理**:`malloc.c`和`free.c`等文件中...
* <cstddef>:定义宏 NULL 和 offsetof,以及其他标准类型 size_t 和 ptrdiff_t。 * <limits>:提供与基本数据类型相关的定义。 * <climits>:提供与基本整数数据类型相关的 C 样式定义。 * <cfloat>:提供与...
1. **基本类型**:定义了整型(如 int, long, short)、浮点型(如 float, double)和其他基本数据类型,以及相关宏和类型定义,如 size_t 和 ptrdiff_t。 2. **输入/输出**:提供标准 I/O 流,如 FILE 结构体,...
使用标准的C库类型(如size_t和ptrdiff_t)来确保兼容性。 - **内存管理**:跨平台库应避免依赖特定平台的内存分配函数,如malloc和free,而应使用跨平台的内存管理接口。 - **错误处理**:使用标准的errno或自定义...
例如,中定义了size_t和ptrdiff_t类型,用于表示对象的大小和两个指针之间的差值。NULL宏用于表示空指针。另外,中定义了FILE类型,它用于操作流。而定义了用于报告错误的errno变量和相关的宏,如EDOM和ERANGE,它们...
11. 标准定义库:定义了几个重要的类型定义和宏,比如size_t和ptrdiff_t,这些类型通常用来表示大小和偏移量。 12. 区域设置库:允许程序在运行时根据不同的地理位置改变其行为,比如日期格式、时间格式、字符编码...
1. **基本类型和宏**:定义了C语言的基本数据类型,如size_t、ptrdiff_t等,以及各种宏定义,如NULL、offsetof等。 2. **内存管理**:提供malloc、calloc、realloc、free等函数,用于动态分配和释放内存。 3. **...
using difference_type = std::ptrdiff_t; using pointer = T*; using reference = T&; }; // Specialization for const_Tp* template<typename T> struct iterator_traits<const T*> { using iterator_...
5. **数据类型使用**:推荐使用标准的类型如`size_t`和`ptrdiff_t`处理大小和差异,以保证跨平台兼容性。避免使用隐式类型转换,尽量显式转换,以减少类型不匹配问题。 6. **函数设计**:函数应尽可能单一职责,...
- 使用`size_t`表示大小或索引,`ptrdiff_t`表示差异。 10. **并发编程**: - 使用`std::mutex`和`std::lock_guard`等工具确保线程安全。 - 避免数据竞争,确保同步正确。 以上仅是Google C++ Style Guide的...
例如,`<stddef.h>`是基础,定义了一些通用类型,如`size_t`用于表示内存大小,`ptrdiff_t`表示指针间的差值,以及`wchar_t`用于宽字符处理,还有符号常量`NULL`代表空指针。`<errno.h>`定义了错误处理相关的`errno`...
6. **数据类型**:使用标准的类型如`size_t`和`ptrdiff_t`处理大小和距离,避免整数溢出。避免使用`int`作为指针差值,应使用`ptrdiff_t`。 7. **避免全局变量**:全局变量可能导致难以调试的问题。尽可能将数据...
- `void* sbrk(ptrdiff_t increment);`:该函数用于增长或缩小进程的数据段,返回值是指向新分配内存的指针。当`increment`为正数时,数据段增长;为负数时,数据段缩小。 - `int brk(void* end_data_segment);`:...
- `t`: 表示`ptrdiff_t`。 - `z`: 表示`size_t`。 **表三:`printf()`的标志** - `-`: 左对齐。 - `+`: 强制显示符号。 - `空格`: 正数前添加空格。 - `#`: 使用替代输出形式。 - `0`: 使用前导零填充。 ##### ...
stddef.h头文件提供了多个宏定义,例如NULL、offsetof、ptrdiff_t、size_t、wchar_t等,用于定义标准的数据类型和宏。 8. stdio.h - 标准输入输出头文件 stdio.h头文件提供了多个函数,例如printf、scanf、puts、...