stackoverflow热门问题(二)- 如何确定C的数组的大小

stackoverflow热门问题目录

如果翻译有问题,麻烦评论提醒一下,谢谢。

如何确定C的数组的大小?

  • Mark Harrison 提问:
    • 我该如何确定C里面数组的大小?
    • 即数组里面能容纳多少个元素?
  • 22 回答:
    • Mark Harrison – 1394 位用户认为有用:
      • 摘要:
        • int a[17];
        • size_t n = sizeof(a)/sizeof(a[0]);
      • 完整答案:
      • 数组所占空间的大小(字节),可以通过sizeof操作来确定。
        • int a[17];
        • size_t n = sizeof(a)/sizeof(a[0]);
      • 在我的计算机中,int占4个字节,所以n等于68。
      • 数组能容纳元素的个数,可以通过数组总大小除以数组元素大小来确定。数组元素的大小可以用它的类型大小表示,像这样:
        • int a[17];
        • size_t n = sizeof(a) / sizeof(int);
      • 就可以得到正确的答案(68 / 4 = 17),但如果类型改变且又忘记改变sizeof(int)里的int,那就会出现个讨厌的bug。
      • 所以最好是将sizeof(a[0])作为除数,或者是sizeof(*a),即数组第一个元素的大小。
        • int a[17];
        • size_t n = sizeof(a) / sizeof(a[0]);
      • 另一个挺方便的方法,是定义一个宏,并将数组名参数化,像这样:
        • #define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
        • int a[17];
        • size_t n = NELEMS(a);
    • Elideb – 883 位用户认为有用:
      • 在处理不是作为函数参数的数组时,sizeof的方式的确没错。数组作为函数的参数时会被视为指针,所以sizeof将会返回指针的大小,而不是数组的大小。
      • 因此,这个方法在函数内部没有效果。而为了获知数组的大小,最好再加一个size_t size作为函数的参数。
      • 例如下面的代码测试中所示,
        • 对于作为参数的数组指针,它返回的是Int指针的大小,
        • 而在函数外部对数组求大小,返回的是整个数组占据的大小,
        • 在译者的电脑(Windows 64位)中,int元素的大小是4,而int指针的大小是8,
        • 即便不是作为参数,而是新定义一个指针指向数组,亦是如此,
        • 这也解释了为什么答主的64位Linux显示Length of parameter等于2而不是1,
        • 因为8 / 4 = 2,
        • 这个问题让译者在LeetCode上纠结了一会:
        • #include <stdio.h>
        • #include <stdlib.h>
        • void printSizeOf(int intArray[]);
        • void printLength(int intArray[]);
        • int main(int argc, char* argv[])
        • {
        • int array[] = { 0, 1, 2, 3, 4, 5, 6 };
        • printf("sizeof of array: %d\n", (int) sizeof(array));
        • printSizeOf(array);
        • printf("Length of array: %d\n", (int)( sizeof(array) / sizeof(array[0]) ));
        • printLength(array);
        • }
        • void printSizeOf(int intArray[])
        • {
        • printf("sizeof of parameter: %d\n", (int) sizeof(intArray));
        • }
        • void printLength(int intArray[])
        • {
        • printf("Length of parameter: %d\n", (int)( sizeof(intArray) / sizeof(intArray[0]) ));
        • }
      • 在64位Linux系统中的输出:
        • sizeof of array: 28
        • sizeof of parameter: 8
        • Length of array: 7
        • Length of parameter: 2
      • 在32位Linux系统中的输出:
        • sizeof of array: 28
        • sizeof of parameter: 4
        • Length of array: 7
        • Length of parameter: 1
    • Magnus Hoff– 144 位用户认为有用:
      • 值得一提的是,sizeof在处理指向数组的指针时并没有效果,哪怕它指向的是数组的首位,因为编译器把数组中的单个元素当做指针来对待。因为一个指针并不会“记得”任何关于它被初始化成数组的事。
        • int a[10];
        • int* p = a;
        • assert(sizeof(a) / sizeof(a[0]) == 10);
        • assert(sizeof(p) == sizeof(int*));
        • assert(sizeof(*p) == sizeof(int));
    • unwind – 51 位用户认为有用:
      • sizeof是我知道最好的技巧,不过在括号的使用上,有一个很小但很重要的改变(对我来说,这个是最令我烦恼的)。
      • 维基百科的条目中解释的很清楚,C的sizeof不是一个函数,而是一个运算符。因此,并不需要在参数旁边加括号,除非这个参数是个类型名称。这很容易记下来,因为它使得参数看起来像个强制转换表达式,也使用括号的那种。
      • 所以:如果你这么定义数组:
        • int myArray[10];
      • 你可以这样来求它的元素个数:
        • size_t n = sizeof myArray / sizeof *myArray;
      • 这对我来说,比带括号的形式好理解多了。我也赞成在除法的右边使用星号,因为这比用下标要简洁。
      • 当然,这都是编译时的,所以不用担心除法会影响程序的性能。所以尽可能的用这种形式。
      • 最好将sizeof用在实际对象里,而不是类型,因为这样不用担心造成bug或写成错误的类型。
      • 例如,假设你有一个函数,通过输出一些字节流数据的函数。我们调用send(),并且将参数设为一个指向待发送对象的指针,以及这个对象占据的字节数。所以,原型变为:
        • void send(const void *object, size_t size);
      • 需要发送一个整型时,可以这样做:
        • int foo = 4711;
        • send(&foo, sizeof (int));
      • 现在,你已经知道了一个能搬起石头砸自己的脚的办法,即在两个地方制定foo的类型。如果只有一个改变而另一个没有,代码会出错。因此,习惯这么做:
        • send(&foo, sizeof foo);
      • 现在没问题了。没错,你可以复制变量名,但如果你改变了它,很可能会被编译器检测出错。

How do I determine the size of my array in C?

  • Mark Harrison asked:
    • How do I determine the size of my array in C?
      • 我该如何确定C里面数组的大小?
    • That is, the number of elements the array can hold?
      • 即数组里面能容纳多少个元素?
  • 22 Answers:
    • Mark Harrison – 1394 people think it useful:
      • Executive summary:
        • 摘要:
        • int a[17];
        • size_t n = sizeof(a)/sizeof(a[0]);
      • Full answer:
        • 完整答案:
      • To determine the size of your array in bytes, you can use the sizeof operator:
        • 数组所占空间的大小(字节),可以通过sizeof操作来确定。
        • int a[17];
        • size_t n = sizeof(a)/sizeof(a[0]);
      • On my computer, ints are 4 bytes long, so n is 68.
        • 在我的计算机中,int占4个字节,所以n等于68。
      • To determine the number of elements in the array, we can divide the total size of the array by the size of the array element. You could do this with the type, like this:
        • 数组能容纳元素的个数,可以通过数组总大小除以数组元素大小来确定。数组元素的大小可以用它的类型大小表示,像这样:
        • int a[17];
        • size_t n = sizeof(a) / sizeof(int);
      • and get the proper answer (68 / 4 = 17), but if the type of a changed you would have a nasty bug if you forgot to change the sizeof(int) as well.
        • 就可以得到正确的答案(68 / 4 = 17),但如果类型改变且又忘记改变sizeof(int)里的int,那就会出现个讨厌的bug。
      • So the preferred divisor is sizeof(a[0]) or the equivalent sizeof(*a), the size of the first element of the array.
        • 所以最好是将sizeof(a[0])作为除数,或者是sizeof(*a),即数组第一个元素的大小。
        • int a[17];
        • size_t n = sizeof(a) / sizeof(a[0]);
      • Another advantage is that you can now easily parameterize the array name in a macro and get:
        • 另一个挺方便的方法,是定义一个宏,并将数组名参数化,像这样:
        • #define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
        • int a[17];
        • size_t n = NELEMS(a);
    • Elideb – 883 people think it useful:
      • The sizeof way is the right way iff you are dealing with arrays not received as parameters. An array sent as a parameter to a function is treated as a pointer, so sizeof will return the pointer’s size, instead of the array’s.
        • 在处理不是作为函数参数的数组时,sizeof的方式的确没错。数组作为函数的参数时会被视为指针,所以sizeof将会返回指针的大小,而不是数组的大小。
      • Thus, inside functions this method does not work. Instead, always pass an additional parameter size_t size indicating the number of elements in the array.
        • 因此,这个方法在函数内部没有效果。而为了获知数组的大小,最好再加一个size_t size作为函数的参数。
      • Test:
        • 例如下面的代码测试中所示,
        • 对于作为参数的数组指针,它返回的是Int指针的大小,
        • 而在函数外部对数组求大小,返回的是整个数组占据的大小,
        • 在译者的电脑(Windows 64位)中,int元素的大小是4,而int指针的大小是8,
        • 即便不是作为参数,而是新定义一个指针指向数组,亦是如此,
        • 这也解释了为什么答主的64位Linux显示Length of parameter等于2而不是1,
        • 因为8 / 4 = 2,
        • 这个问题让译者在LeetCode上纠结了一会:
        • #include <stdio.h>
        • #include <stdlib.h>
        • void printSizeOf(int intArray[]);
        • void printLength(int intArray[]);
        • int main(int argc, char* argv[])
        • {
        • int array[] = { 0, 1, 2, 3, 4, 5, 6 };
        • printf("sizeof of array: %d\n", (int) sizeof(array));
        • printSizeOf(array);
        • printf("Length of array: %d\n", (int)( sizeof(array) / sizeof(array[0]) ));
        • printLength(array);
        • }
        • void printSizeOf(int intArray[])
        • {
        • printf("sizeof of parameter: %d\n", (int) sizeof(intArray));
        • }
        • void printLength(int intArray[])
        • {
        • printf("Length of parameter: %d\n", (int)( sizeof(intArray) / sizeof(intArray[0]) ));
        • }
      • Output (in a 64-bit Linux OS):
        • 在64位Linux系统中的输出:
        • sizeof of array: 28
        • sizeof of parameter: 8
        • Length of array: 7
        • Length of parameter: 2
      • Output (in a 32-bit windows OS):
        • 在32位Linux系统中的输出:
        • sizeof of array: 28
        • sizeof of parameter: 4
        • Length of array: 7
        • Length of parameter: 1
    • Magnus Hoff– 144 people think it useful:
      • It is worth noting that sizeof doesn’t help when dealing with an array value that has decayed to a pointer: even though it points to the start of an array, to the compiler it is the same as a pointer to a single element of that array. A pointer does not “remember” anything else about the array that was used to initialize it.
        • 值得一提的是,sizeof在处理指向数组的指针时并没有效果,哪怕它指向的是数组的首位,因为编译器把数组中的单个元素当做指针来对待。因为一个指针并不会“记得”任何关于它被初始化成数组的事。
        • int a[10];
        • int* p = a;
        • assert(sizeof(a) / sizeof(a[0]) == 10);
        • assert(sizeof(p) == sizeof(int*));
        • assert(sizeof(*p) == sizeof(int));
    • unwind – 51 people think it useful:
      • The sizeof “trick” is the best way I know, with one small but (to me, this being a major pet peeve) important change in the use of parenthesis.
        • sizeof是我知道最好的技巧,不过在括号的使用上,有一个很小但很重要的改变(对我来说,这个是最令我烦恼的)。
      • As the Wikipedia entry makes clear, C’s sizeof is not a function; it’s an operator. Thus, it does not require parenthesis around its argument, unless the argument is a type name. This is easy to remember, since it makes the argument look like a cast expression, which also uses parenthesis.
        • 维基百科的条目中解释的很清楚,C的sizeof不是一个函数,而是一个运算符。因此,并不需要在参数旁边加括号,除非这个参数是个类型名称。这很容易记下来,因为它使得参数看起来像个强制转换表达式,也使用括号的那种。
      • So: If you have the following:
        • 所以:如果你这么定义数组:
        • int myArray[10];
      • You can find the number of elements with code like this:
        • 你可以这样来求它的元素个数:
        • size_t n = sizeof myArray / sizeof *myArray;
      • That, to me, reads a lot easier than the alternative with parenthesis. I also favor use of the asterisk in the right-hand part of the division, since it’s more concise than indexing.
        • 这对我来说,比带括号的形式好理解多了。我也赞成在除法的右边使用星号,因为这比用下标要简洁。
      • Of course, this is all compile-time too, so there’s no need to worry about the division affecting the performance of the program. So use this form wherever you can.
        • 当然,这都是编译时的,所以不用担心除法会影响程序的性能。所以尽可能的用这种形式。
      • It is always best to use sizeof on an actual object when you have one, rather than on a type, since then you don’t need to worry about making an error and stating the wrong type.
        • 最好将sizeof用在实际对象里,而不是类型,因为这样不用担心造成bug或写成错误的类型。
      • For instance, say you have a function that outputs some data as a stream of bytes, for instance across a network. Let’s call the function send(), and make it take as arguments a pointer to the object to send, and the number of bytes in the object. So, the prototype becomes:
        • 例如,假设你有一个函数,通过输出一些字节流数据的函数。我们调用send(),并且将参数设为一个指向待发送对象的指针,以及这个对象占据的字节数。所以,原型变为:
        • void send(const void *object, size_t size);
      • And then you need to send an integer, so you code it up like this:
        • 需要发送一个整型时,可以这样做:
        • int foo = 4711;
        • send(&foo, sizeof (int));
      • Now, you’ve introduced a subtle way of shooting yourself in the foot, by specifying the type of foo in two places. If one changes but the other doesn’t, the code breaks. Thus, always do it like this:
        • 现在,你已经知道了一个能搬起石头砸自己的脚的办法,即在两个地方制定foo的类型。如果只有一个改变而另一个没有,代码会出错。因此,习惯这么做:
        • send(&foo, sizeof foo);
      • Now you’re protected. Sure, you duplicate the name of the variable, but that has a high probability of breaking in a way the compiler can detect, if you change it.
        • 现在没问题了。没错,你可以复制变量名,但如果你改变了它,很可能会被编译器检测出错。

You may also like...

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注