[英]What type is used in C++ to define an array size?
在avr-gcc
為一個8位微控制器編譯一些測試代碼
const uint32_t N = 65537;
uint8_t values[N];
我得到了以下編譯警告(默認情況下應該是一個錯誤,真的)
warning: conversion from 'long unsigned int' to 'unsigned int' changes value from '65537' to '1' [-Woverflow]
uint8_t values[N];
請注意,在為此目標進行編譯時, sizeof(int)
為2。
所以看來,數組大小不能超過unsigned int
的大小。
我對么? 這是GCC特定的還是某些C或C ++標准的一部分?
在有人評論說8位微控制器通常沒有足夠的內存來容納如此大的陣列之前,讓我只是期待說這不是重點。
size_t
被認為是要使用的類型,盡管沒有被C或C ++標准正式批准。
這樣做的理由是, sizeof(values)
將是類型(即是由C和C ++標准mandatated),和元素的數量將是必然因為比這不大於sizeof
用於一個目的是至少1。
所以看來,數組大小不能超過
unsigned int
的大小。
在您的特定C [++]實現中似乎就是這種情況。
我對么? 這是gcc特定的還是某些C或C ++標准的一部分?
它一般不是GCC的特征,也不是C或C ++標准規定的。 這是您特定實現的一個特征:適用於您的特定計算平台的GCC版本。
C標准要求表達式指定數組的元素數量具有整數類型,但它不指定特定的類型。 我覺得奇怪的是,你的GCC似乎聲稱它給你一個陣列,其元素數量與你指定的數量不同。 我不認為這符合標准,我不認為它作為擴展是有意義的。 我寧願看到它拒絕代碼。
在您的實現中, size_t
定義為unsigned int
, uint32_t
定義為long unsigned int
。 創建C數組時,編譯器size_t
數組大小的參數隱式轉換為size_t
。
這就是你收到警告的原因。 您正在使用uint32_t
指定數組大小參數,該參數將轉換為size_t
並且這些類型不匹配。
這可能不是你想要的。 請改用size_t
。
我將用“incorrekt and incomplet”ISO CPP標准草案n4659中的規則解決問題。 我強調了重點。
11.3.4定義數組聲明。 第一段包含
如果存在常量表達式[方括號之間](8.20),則它應該是std :: size_t [...]類型的轉換常量表達式 。
std::size_t
來自<cstddef>
並定義為
[...]一個實現定義的無符號整數類型,其大小足以包含任何對象的字節大小。
由於它是通過C標准庫頭導入的,因此C標准與size_t
的屬性相關。 如果需要,ISO C草案N2176在7.20.3中規定了“最小最大值”,即整數類型。 對於size_t
,最大值為65535.換句話說,16位size_t
完全符合。
“轉換的常量表達式”在8.20 / 4中定義:
轉換后的常量類型T表達式是一個表達式,隱式轉換為類型T,其中轉換后的表達式是常量表達式,隱式轉換序列僅包含[10個不同轉換中的任何一個,其中一個涉及整數(參數4.7): ]
- 除了縮小轉換次數之外的積分轉換(7.8)(11.6.4)
整數轉換 (與將類型更改為等效或更大類型的促銷相反)定義如下(7.8 / 3):
可以將整數類型的prvalue轉換為另一個整數類型的prvalue。
7.8 / 5然后排除積分轉換的整體促銷 。 這意味着轉換通常會縮小類型更改。
縮小轉換 (正如您將記得的那樣,從用於數組大小的轉換常量表達式中的允許轉換列表中排除)在list-initialization,11.6.4,par。的上下文中定義。 7
縮小轉換是隱式轉換
[...]
7.3 1 - 從整數類型[...]到不能表示原始類型的所有值的整數類型, 除非源是一個常量表達式,其整數提升后的值將適合目標類型。
這實際上是說有效數組大小必須是顯示的常數值,這是避免意外的完全合理的要求。
std::size_t
是16位無符號整數類型,其值范圍為0..65535。
整數文字65537
在系統的16位unsigned int
整數中無法表示,因此類型為long
。
因此它將進行整數轉換。
這將是一個縮小的轉換,因為該值在16位size_t
2中無法表示,因此11.6.4 / 7.3中的異常條件“無論如何都適合”,不適用。
那么這是什么意思?
11.6.4 / 3.11是無法從初始化列表中的項生成初始化值的全部捕獲規則。 因為初始化列表規則用於數組大小,我們可以假設轉換失敗的全部屬性適用於數組大小常量:
(3.11) - 否則,該程序是不正確的。
需要一個符合要求的編譯器來生成診斷程序。 案件結案。
2將一個整數值65537(無論何種類型都可以保存數字 - 這里可能是一個`long)轉換為16位無符號整數是一個已定義的操作。 7.8 / 2詳細信息:
如果目標類型是無符號的,則結果值是與源整數一致的最小無符號整數(模2 n ,其中n是用於表示無符號類型的位數)。 [注意:在二進制補碼表示中,此轉換是概念性的,並且位模式沒有變化(如果沒有截斷)。 - 尾注]
65537的二進制表示是1_0000_0000_0000_0001
,即僅設置低16位的最低有效位。 轉換為16位無符號值(間接證據表明size_t
為)計算[表達式]模2 ^ 16,即簡單地取低16位。 這導致編譯器診斷中提到的值1。
sizeof
返回的值的類型為size_t
。
它通常用作數組中元素的數量,因為它的大小足夠。 size_t
始終是無符號的,但它是實現定義的類型。 最后,實現定義了實現是否可以支持甚至SIZE_MAX
字節的對象......甚至接近它。
[這個答案是在用C和C ++標記問題時編寫的。 我還沒有根據OP的啟示重新審視它,他們使用的是C ++而不是C.
size_t
是C標准用於處理對象大小的類型。 然而,它並不能解決所有尺寸問題。
size_t
應該在<stddef.h>
頭文件中定義(以及在其他頭文件中)。
當在聲明中指定時,C標准不要求數組大小的表達式具有size_t
類型,也不要求它們適合size_t
。 當C實現不能滿足對數組大小的請求時,特別是對於可變長度數組,沒有指定C實現應該做什么。
在你的代碼中:
const uint32_t N = 65537;
uint8_t values[N];
values
被聲明為可變長度數組。 (雖然我們可以看到N
的值在編譯時很容易知道,但它不符合C對常量表達式的定義,因此uint8_t values[N];
有資格作為可變長度數組的聲明。)正如您所觀察到的那樣, GCC警告您,32位無符號整數N
縮小為16位無符號整數。 C標准不要求此警告; 它是由編譯器提供的禮貌。 更重要的是,根本不需要轉換 - 因為C標准沒有指定數組維度的類型,編譯器可以在這里接受任何整數表達式。 因此,它已經將隱式轉換插入到數組維度所需的類型並警告您這一事實是編譯器的一個特性,而不是C標准的特性。
考慮如果你寫下會發生什么:
size_t N = 65537;
uint8_t values[N];
現在uint8_t values[N];
沒有警告uint8_t values[N];
,作為一個16位整數(C實現中size_t
的寬度)正在使用需要16位整數的地方。 但是,在這種情況下,您的編譯器可能會在size_t N = 65537;
警告size_t N = 65537;
因為65537
將具有32位整數類型,並且在N
的初始化期間執行縮小轉換。
但是,您使用可變長度數組的事實表明您可能在運行時計算數組大小,這只是一個簡化的示例。 可能你的實際代碼不使用像這樣的常量大小; 它可以在執行期間計算尺寸。 例如,您可以使用:
size_t N = NumberOfGroups * ElementsPerGroup + Header;
在這種情況下,可能會計算出錯誤的結果。 如果變量都具有類型size_t
,則結果可能很容易換行(有效地溢出size_t
類型的限制)。 在這種情況下,編譯器不會給你任何警告,因為這些值都是相同的寬度; 沒有縮小轉換,只是溢出。
因此,使用size_t
不足以防止數組維度中的錯誤。
另一種方法是使用你期望足夠寬的類型進行計算,也許是uint32_t
。 給定NumberOfGroups
和uint32_t
類型,然后:
const uint32_t N = NumberOfGroups * ElementsPerGroup + Header;
將為N
生成正確的值。 然后,您可以在運行時測試它以防止錯誤:
if ((size_t) N != N)
Report error…
uint8_t values[(size_t) N];
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.