简体   繁体   English

Int 数组在 c 中接受比声明大小多 1 个元素

[英]Int array accept 1 more element in c than declared size

I have an integer array with size 5 .我有一个大小为 5 的 integer 数组 The problem is that the array accept 6 elements as opposed to 5 .问题是数组接受6 个元素而不是5 But when i print the array out i get the desired output.但是当我打印出数组时,我得到了所需的 output。 ie; IE; with 5 elements.5 个元素。 Why does this happen?为什么会这样?

These were some of the questions which showed some similariy to my question... but the fact is that these are somewhat advanced for me with classes, structures ect... And also most of these questions talked about character array but my question is of integer array (PS: I don't know if that really matters)这些是一些与我的问题有些相似的问题......但事实是这些对我来说在类、结构等方面有些先进......而且这些问题中的大多数都谈到了字符数组,但我的问题是integer 数组(PS:我不知道这是否真的重要)

Array with only 1 element storing more than it should 仅包含 1 个元素的数组存储超出其应有的存储量

Why allocate an array of size 1 more than the requested size? 为什么分配一个比请求大小大 1 的数组?

Array contains more elements than declared size. 数组包含比声明大小更多的元素。 Why does it do that? 为什么这样做?

C program displaying more characters than array size C 程序显示的字符多于数组大小

I tried furthur searching and the closest answer i got was: " Array may not be properly null terminated. " but i don't know anything about integer delimiter except for string delimiter which is "\0" i believe;我尝试进一步搜索,我得到的最接近的答案是:“数组可能没有正确终止 null。 ”但我对 integer 分隔符一无所知,除了字符串分隔符“\0”我相信;

// my code
#include <stdio.h> 

int main(void) {
    int ar[5];
    printf("Enter the array values: ");
    for(int i = 0; i < 5; i++) {
        scanf("%d ", &ar[i]);
    }
    printf("Array elemnts are: ");
    for(int j = 0; j < 5; j++) {
        printf("\n%d", ar[j]);
    }

    return 0;
}

[Output][1] [1]: https://i.stack.imgur.com/Gv13S.png [输出][1][1]:https://i.stack.imgur.com/Gv13S.png

Any help would really be appreciated.任何帮助将不胜感激。 Also sorry if it's poorly described i have never asked question before on stack overflow.也很抱歉,如果描述得不好,我之前从未在堆栈溢出时问过问题。

In C , array bounds are not checked in any way.C中,不以任何方式检查数组边界。 Therefore, you can freely try to access an element outside the size of the array.因此,您可以自由地尝试访问数组大小之外的元素。 However, doing so is undefined behavior and may cause your computer to shoot demons out of your nose.但是,这样做是未定义的行为,可能会导致您的计算机从您的鼻子射出恶魔。

So, while working on arrays, you must yourself ensure that you only access elements in the array.因此,在处理 arrays 时,您必须自己确保仅访问数组中的元素。 You will get no error from the compiler if you do this wrong.如果你做错了,编译器不会出错。

As funny as it sounds (been trying to figure out why it was not working - I was desperate to know what caused this) i have found the problem or issue that caused this.听起来很有趣(一直试图弄清楚为什么它不起作用 - 我很想知道是什么原因造成的)我发现了导致这种情况的问题或问题。 This was caused because of the space after %d in the scanf statement.这是由于scanf语句中%d之后的空格引起的。 When that space was removed i was able to input and output exactly 5 elements.删除该空间后,我可以输入 output 正好 5 个元素。 Anyway thanks to all.总之谢谢大家。

[Before removing the space:] https://i.stack.imgur.com/Gv13S.png 【去掉空格前:】 https://i.stack.imgur.com/Gv13S.png

[After removing the space:] https://i.stack.imgur.com/TvdvG.png 【去掉空格后:】 https://i.stack.imgur.com/TvdvG.png

C does not feature boundary-checked types, since it would introduce a lot more instructions at run-time and - in some cases - limit some of the use-cases of C. C 不具有边界检查类型,因为它会在运行时引入更多指令,并且在某些情况下会限制 C 的一些用例。

In many cases, arrays are transparently converted into pointers.在很多情况下,arrays 被透明地转换为指针。 The square-brackets index operator is largely a wrapper around pointer arithmetic, so understanding pointers is an important aspect of understanding C and its behaviours, so I'll do my best to explain them.方括号索引运算符在很大程度上是指针算法的包装,因此理解指针是理解 C 及其行为的重要方面,所以我会尽力解释它们。

When you declare a variable of a type in one of your functions (or globally), your program reserves a certain amount of memory for that type.当您在其中一个函数(或全局)中声明某种类型的变量时,您的程序会为该类型保留一定数量的 memory。 When you declare an array of a variable type, the program reserves enough memory for that many elements.当您声明一个变量类型的数组时,程序会为这么多元素保留足够的 memory。

The C standard only defines a fixed size for the char type - 1 byte. C 标准仅定义了char类型的固定大小 - 1 个字节。 other types, such as int , can be defined as different sizes depending on the compiler or the processor your compiler is building for - typically based on the number of bytes the processor can efficiently operate with.其他类型,例如int ,可以定义为不同的大小,具体取决于编译器或编译器正在构建的处理器 - 通常基于处理器可以有效运行的字节数。

For example, a 32-bit processor can efficiently add, multiply, integer divide and deduct 32-bit numbers, but it might take multiple operations for it to do similar computations with 64-bit numbers - for example, by adding the first 32-bit parts, storing any carry-over values, adding the second two 32-bit parts and the carry.例如,一个 32 位处理器可以有效地加、乘、integer 除和减去 32 位数字,但它可能需要多次操作才能对 64 位数字进行类似的计算——例如,通过将前 32-位部分,存储任何结转值,将后两个 32 位部分和进位相加。

For a 32-bit processor, then, an int is usually defined as 4 bytes, or (32 bits)/(8 bits per byte).那么,对于 32 位处理器,一个int通常定义为 4 个字节,或(32 位)/(每字节 8 位)。 Similarly, 64-bit processors may use a 64-bit int , etc.类似地,64 位处理器可以使用 64 位int等。

You can find out the sizes of types for your compiler/processor combination with the sizeof operator:您可以使用sizeof运算符找出编译器/处理器组合的类型大小:

#include <stdio.h>

int main()
{
    printf("%ld\n", sizeof(int));
}

On a 64-bit x86 processor - processors running some version of Intel's x86 assembly, including desktop/laptop AMD processors - the above code will print 4 , even for 64-bit processors.在 64 位 x86 处理器上 - 处理器运行英特尔 x86 程序集的某些版本,包括台式机/笔记本电脑 AMD 处理器 - 上述代码将打印4 ,即使对于 64 位处理器也是如此。 This is for technical reasons I won't go into, but hopefully you get the idea.这是出于技术原因,我不会 go 进入,但希望你明白这一点。

So when you declare a variable as an array of 4 char s, your function reserves 4 * 1 = 4 bytes for it, starting at the beginning of the array, with each element right next to each other.因此,当您将变量声明为 4 个char的数组时,您的 function 为它保留4 * 1 = 4个字节,从数组的开头开始,每个元素彼此相邻。 Similarly, when you declare a variable of 4 int s, it reserves 4 * 4 = 16 bytes, one next to the other.同样,当您声明一个 4 int的变量时,它会保留4 * 4 = 16个字节,一个接一个。

Below is a visualization of what happens in memory when you declare arrays of different sizes/types:下面是当您声明不同大小/类型的 arrays 时 memory 中发生的情况的可视化:

+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
|char foo[4]    | 1st | 2nd | 3rd | 4th | --- | --- | --- | --- |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
|int foo[2]     | 1st | 1st | 1st | 1st | 2nd | 2nd | 2nd | 2nd |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+

Now lets take a look at how C pointers help you move around memory.现在让我们看看 C 指针如何帮助您移动 memory。

A pointer is a variable which stores two bits of information:指针是存储两位信息的变量:

  • The location in memory we're interested in; memory中我们感兴趣的位置;
  • What to treat that variable like.怎样对待这个变量。

It can be confusing to reason about, because it's a variable which stores two bits of data, so lets start with a pointer which only stores a memory address - void * .推理可能会令人困惑,因为它是一个存储两位数据的变量,所以让我们从一个只存储 memory 地址的指针开始 - void *

We can get the address of a variable with the & operator, and print if with printf("%p") :我们可以使用&运算符获取变量的地址,并使用printf("%p")打印 if:

#include <stdio.h>

int main()
{
    int a = 0;
    int b = 42;
    
    void *a_pointer = &a;
    void *b_pointer = &b;
    
    printf("%p: %d\n", a_pointer, a);
    printf("%p: %d\n", b_pointer, b);
}

On my system, the above program prints something like this:在我的系统上,上面的程序打印如下内容:

~/playground$ ./pointers 
0x7ffd54bda370: 0
0x7ffd54bda374: 42
~/playground$

As you can see, the program has put a and b right next to each other - b 's memory address ends with ...374 , 4 bytes more than a 's address ...370 .如您所见,程序将ab放在一起 - b的 memory 地址以...374结尾,比a的地址...3704个字节。

The compiler and operating system make decisions about how to (re-)arrange variables and align them to make them quick to access - in this case, it's decided that placing a and b right next to each other results in the fastest access and lowest memory consumption.编译器和操作系统决定如何(重新)排列变量并对齐它们以使它们快速访问 - 在这种情况下,它决定将ab彼此相邻放置导致最快的访问和最低的 memory消耗。

Lets take a look at a similar program with an array of integers instead:让我们来看看一个类似的程序,它有一个整数数组:

#include <stdio.h>

int main()
{
    int a[2] = { 0, 42 };

    void *a_first = &a[0];
    void *a_second = &a[1];

    printf("%p: %d\n", a_first, a[0]);
    printf("%p: %d\n", a_second, a[1]);
}

And lets see what it prints:让我们看看它打印了什么:

~/playground$ ./pointers 
0x7ffe51de1fb0: 0
0x7ffe51de1fb4: 42
~/playground$

If you look at the memory addresses, you can see the same thing: the first is at address ...fb0 , while the second is at address ...fb4 , 4 bytes after.如果您查看 memory 地址,您可以看到相同的内容:第一个位于地址...fb0 ,而第二个位于地址...fb4 ,之后的 4 个字节。

There are important differences between these two cases:这两种情况有重要区别:

  • In the first program, we didn't make any decisions about how the variables should be arranged - in functions with more variables, and variables of different sizes, the compiler is allowed to re-arrange them in memory arbitrarily, if the overall effect is the same and it allows the program to run faster.在第一个程序中,我们没有对变量应该如何排列做出任何决定——在变量较多的函数中,以及不同大小的变量中,允许编译器在 memory 中任意重新排列它们,如果整体效果是同样,它允许程序运行得更快。 That means we can't rely on the arrangement of those variables in memory.这意味着我们不能依赖 memory 中这些变量的排列。
  • In the second program, we explicitely told the compiler to put the two integers next to each other.在第二个程序中,我们明确告诉编译器将两个整数放在一起。 No matter what else is in the function, we have a guarantee that a[1] will always be placed 1 int after a[0] .无论 function 中还有什么内容,我们都保证a[1]将始终放置在a[0]之后的 1 个int处。

We've looked at the addresses of these raw pointers - and that's about as much as we can do with them, since C doesn't know how to operate on them or their values.我们已经查看了这些原始指针的地址——这大约是我们可以用它们做的,因为 C 不知道如何对它们或它们的值进行操作。 To tell C what is possible with these pointers, and what we can do with the pointed-to values, we need to tell it the type pointed to.要告诉 C 这些指针有什么可能,以及我们可以用指向的值做什么,我们需要告诉它指向的类型。

Now we can do things like get the value from the pointer directly - we couldn't do that before, because C has no idea how much memory to access from void * .现在我们可以做一些事情,比如直接从指针中获取值——我们以前不能这样做,因为 C 不知道从void *访问多少 memory 。

#include <stdio.h>

int main()
{
    int a[2] = { 0, 42 };
    
    int *a_pointer = &a[0];
    
    printf("%p: %d\n", a_pointer, *a_pointer);
}

With the information that a_pointer points to an int , C now knows to only read 4 bytes when we ask for the value.有了a_pointer指向int的信息,C 现在知道当我们请求值时只读取 4 个字节。 You can see in the printf statement that we can ask for the value with the star operator * .您可以在printf语句中看到我们可以使用星号运算符*请求该值。 We could also write to the value, by doing *a_pointer = 84;我们也可以通过*a_pointer = 84;写入值。 for example.例如。

That's not all we can do with this pointer - we can "seek through" memory by increasing or decreasing this pointer.这不是我们可以用这个指针做的全部——我们可以通过增加或减少这个指针来“寻找”memory。

#include <stdio.h>

int main()
{
    int a[2] = { 0, 42 };
    
    int *a_pointer = &a[0];
    
    printf("%p: %d\n", a_pointer, *a_pointer);
    
    // re-assign the pointer to the next `int`
    a_pointer++;
    printf("%p: %d\n", a_pointer, *a_pointer);
    
    // a_pointer-- works too
    a_pointer--;
    // You can also do standard arithmetic on it
    printf("%p: %d\n", a_pointer + 1, *(a_pointer + 1));
}
~/playground$ ./pointers 
0x7fff23cc1b10: 0
0x7fff23cc1b14: 42
0x7fff23cc1b14: 42
~/playground$

Take a look at the last thing we do in the last prinft statement - *(a_pointer + 1) .看看我们在最后一个prinft语句中所做的最后一件事 - *(a_pointer + 1) This is actually exactly the same thing C does when you use square-brackets to seek through an array - a[1] == *(a_pointer + 1) .这实际上与 C 在您使用方括号查找数组时所做的完全相同 - a[1] == *(a_pointer + 1)

In fact, I've been doing int *a_pointer = &a[0];其实我一直在做int *a_pointer = &a[0]; for each of these programs - getting the memory address of the first member of the a array - when you can actually just assign the pointer's value directly to the array: int *a_pointer = a;对于这些程序中的每一个 - 获取a数组第一个成员的 memory 地址 - 当您实际上可以直接将指针的值分配给数组时: int *a_pointer = a; . . You can even use the square-brackets access on the pointer itself to do exactly the same thing:您甚至可以使用指针本身的方括号访问来执行完全相同的操作:

#include <stdio.h>
  
int main()
{
    int a[2] = { 0, 42 };

    int *a_pointer = a;

    printf("%d\n", a[0]);
    printf("%d\n", a_pointer[0]);

    printf("%d\n", a[1]);
    printf("%d\n", a_pointer[1]);
}
~/playground$ ./pointers 
0
0
42
42
~/playground$

Now, finally, we can start to answer your question.现在,终于,我们可以开始回答你的问题了。

Accessing values in an array in C happens as operations on pointers-to-memory.访问 C 中的数组中的值是对内存指针的操作。 The fundamental problem is, we want the program to run as quickly as possible and with as little resource consumption as possible - but that results in situations like the first program, compared to the second: even though we wrote different code, it resulted in a very similar layout in memory.根本问题是,我们希望程序尽可能快地运行,并尽可能减少资源消耗——但这会导致类似于第一个程序的情况,与第二个相比:即使我们编写了不同的代码,它也会导致memory 中的布局非常相似。

If you just looked at the memory addresses those programs printed to console, you would not be able to tell which came from an array of integers and which came from two separate integer variables.如果您只查看 memory 地址打印到控制台的那些程序,您将无法分辨哪些来自整数数组,哪些来自两个单独的 integer 变量。 Introducing logic in the program to check if accesses like that have "overstepped their bounds" - or accessed a different variable, vs. the next element of the same variable - would introduce a lot more run-time logic and reduce the performance of the program overall.在程序中引入逻辑来检查这样的访问是否“越界”——或者访问不同的变量,而不是同一个变量的下一个元素——会引入更多的运行时逻辑并降低程序的性能全面的。

In these simple cases, a human can easily look at the code and say what valid values for the pointers should be - but it gets more complicated in programs (or libraries) which need to operate with different amounts of data.在这些简单的情况下,人们可以轻松地查看代码并说出指针的有效值应该是什么 - 但在需要处理不同数量的数据的程序(或库)中它变得更加复杂。 It may even be that they don't know how much memory they can scan until the user runs the program - a programmer can't know, for example, how many E-mails a user has, or how many friends are in their friends list.在用户运行程序之前,他们甚至可能不知道他们可以扫描多少 memory - 程序员无法知道,例如,用户有多少电子邮件,或者他们的朋友中有多少朋友列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM