c语言指针设计,C语言指针(More on Pointers?)

本文深入讲解了C语言中的指针概念,从硬件层面探讨了CPU地址位与内存地址位的关系。作者通过实例展示了如何在不同宽度的内存设备上进行数据访问,并解释了指针如何存储对象地址以及如何修改这些地址以访问内存。此外,还讨论了指针在结构体和数组中的应用,强调了指针在处理不同类型数据时的注意事项和潜在问题。最后,文章提供了一些练习题以加深读者对指针的理解。
摘要由CSDN通过智能技术生成

This month, George responds to reader requests for a thorough explanation of pointers in C language. He includes several examples and homework assignments. To get you started,George explains the connection between CPU address bits and memory address bits.

I have had several requests for more information about pointers. Well, here it goes. I'll give you more than you ever wanted to know about pointers.

ADDRESSING

Pointers contain the address of an object and can be used to access that object. Traditionally, pointers have been explained by using arrays and showing their similarities. Because that explanation is available in most discussions about the C language, I‘m going to take a different path in explaining pointers.[1]

Let's look at addressing from a hardware point of view because you‘re going to be dealing with the addresses of memory locations. Assume you have an embedded system with a 32-bit CPU (this helps make the data buses large enough to be interesting), a 16-bit data bus (32-bit data fetches must be done in two operations),and a 32-bit address bus. Now assume you have a 16-bit-wide RAM connected to the CPU. The 16-bit data fetched would be done in one read operation. Consider how 8-bit data fetches would be performed. Perhaps your system would be smaller in one or more of these three features, but this discussion won‘t be much different for your particulars.

The CPU can reference 8-bit data elements and that 8-bit data element is the smallest addressable element in the address space. So, CPU address = 0 addresses is the lowest 8-bit entity. CPU address = 1 address is the next highest entity. We have the granularity required on the CPU address bus. But our 16-bit-wide memory doesn't need the least significant bit (LSB) of the CPU address bus connected to the memory device. With our 16-bit data bus, 8-bit data is received by the CPU bus using signals like byte low enable(BLE) or byte high enable (BHE) to transfer (gate) the 8 bits of data from the 16-bit data bus into the CPU registers. The 16-bit-wide memory attached to the address bus reads two 8-bit data elements at a time. The CPU would send an address of 0 to address the first 16-bit location in your memory and send an address of 2 to address the next 16-bit location. The memory would be connected so that the first address is memory location 0 and the second address is memory location 2. The physical connection for the address bus between the CPU and this memory is shifted to the right one address bit.

If an 8-bit memory is connected to the CPU, address bit 0 is used to get to the smallest element. If a 16-bit memory is connected to the CPU,address bit 1 is used to get to the smallest memory element and BHE and BLE are used to chop that 16-bit element into 8-bit elements.

This explanation can be extended to a 32-bitwide memory. This connection of CPU address bits to memory address bits is important to recognize because pointers use addresses relative to the CPU not the memory device. So much for where the addresses come from. It‘s important for you to understand the introduction before I discuss pointers.

POINTERS

A pointer is simply the address of what you are referencing. The pointer will be the CPU address and can be modified by the hardware connections as it gets out to the physical memory device. It merely points to the address of what you are referencing and contains the address of what you are referencing. Simple enough. See you next time.

If you are interested in working with pointers, then read on. I'll give you some examples, but watch out for any homework assignments that may follow. Listing 1 contains some simple statements declaring pointers to 8-bit objects and working with them. Pointer p1 is set to point to data[0]-or,said another way, p1 contains the address of data[0]. This is done using two different statements. Remember the & operator is the "address of" operation. And the address of the entire data array is the same as the address of the first element in the array &data[0]. If you declared an array in memory, what would the linkage map look like? Even in assembly language,that map would tell you the starting address of the data array or the address of the first element in that array. Also in Listing 1 you see that you copy the value of data array element 0 in variable x. This compiles and works with no errors because p1 and x are the same type of storage. Specifically, they both are of type INT8.

2ca4806.jpg

In Listing 1, I tried to put the address of data[99] into p3. The compiler should give a warning. It will compile and produce executable code that does what you told it to do, but the compiler knows that p3 and data[99] are different types. The next statement corrects the warning by informing the compiler to cast the &data[99] to a type of UINT8. So, for just this one statement, you would like to treat data[99] not as an INT8 type,but as a UINT8 type. This second statement would compile with no errors. But now you are mixing data types and give up some of the error checking that the compiler would provide. Finally, look at the statement differencing pointers p1 and p2. It looks like we are just subtracting the two addresses. This should make sense to the more hardware-orientated readers. And from my introduction on addressing, I hope you recognize that it‘s just address math because you are working on the finest element the CPU can address.

In Listing 2, the first for loop zeroes the contents of the data array using an index to access the array. The second for loop zeroes the contents of the data array using a pointer to access the array. Not so long ago, that first method would have compiled to a lot more assembly language code than the second. Think about it: for each loop iteration, the compiler would have to load the address of data[0] and then add i × the size of each data element to that address. In the second method,the pointer is merely advanced by the size of the element the pointer is pointing to. But I'm finding modern compilers treat either approach with the same efficient code. Your first assignment is to check your compiler.

8bc24eac.jpg

Listing 3 contains the same operations as Listing 1, but now we‘re pointing to INT16-type elements-and those 16-bit elements are found every two CPU address. All the comments about the code remain the same until we get to the differencing of the pointers. p2 is pointing to the twenty-fifth element. p1 is pointing to the 0 element,so the difference is 25 elements, not 50, which would be the numerical difference in the value of the address. Your second assignment is to verify this in your system.

9d75bf42.jpg

Another way to look at this is found in Listing 4. In the for loop, the i++ statement must advance the pointer to the next address for the data type being referenced. That must work that way to make sense. The differencing of pointers must also follow those rules. Remember i++ is the same as i = i + 1. But because we‘re referencing 16-bit elements, the i gets converted by the size of the element when the compiler compiles data[i]. Also the p++ statement must advance the pointer p by two since we're referencing 16-bit elements. Compile this code and watch the values of i and p as you step through the instructions.

9a2a5580.jpg

The same extension can be made if you reference INT32 elements (or float elements)。 The address is incremented by four (or the size of the float element) for an increment of the pointer. A pointer is just the address of what you‘re referencing. The compiler knows what you're pointing to and helps out.

Now that you understand pointers a bit more, look at Listing 5. I defined a structure (AIRPLANE_SEATING) that makes room for the names of a pilot, copilot, flight engineer,and 200 passengers. I capitalized it because it is more of a constant than a variable. So now you‘re working with a custom data type of some nonstandard size. Let's see how pointers will work in this situation. Notice how I defined MAX_PLANES but didn‘t use defines for the other array sizes. Perhaps this wasn't such a good idea. What happens when marketing says there‘s a new plane coming with room for 300 passengers?

点击查看Listing 5

You defined a structure to contain all the data (AIRPLANE _SEATING) and set aside memory for (MAX_PLANES) such structures. Next, I defined two pointers p and q. They are going to point to members of the array of structures(AirPlaneSeating[MAX_PLANES])。

The first task is to initialize this array and NULL out the names of pilots. Using an array approach, the first for loop sets the pilot name in each array to NULL by setting the first element in the name to NULL. Remember a string is NULL terminated. This loop uses arrays and array elements to define the exact memory location you‘re operating on.

Using a pointer approach, the next for loop sets all of the pilot names to NULL and all of the passenger names also to NULL. This loop starts with a pointer p being set to the first element (0 element)。 Elements are accessed using the pointer p and the -> operator. That operator accesses structure members using the pointer and the known offset in memory to the desired element. Remember, a pointer is just the address of the object and all the point manipulation is just resolving or calculating the next address.

Next, the inner loop gets a reference for each passenger name (all 200) on that plane. After all the operations are complete, the pointer p is incremented. Think about this a bit. The pointer p is pointing to a user-defined data structure. So, incrementing that pointer would just advance that pointer by the size of each data element. And that size is known at compile time. Thus, it‘s just adding a constant to p. The operator p-> adds a number to the pointer to calculate a specific memory address. Again, if that number is known at compile time, the additional amount will also be a constant.

Look at the differencing of the pointers p and q. The values for j and i again represent the delta in the objects these pointers are referencing, not the numerical difference of the values of the addresses. While this might surprise you (after this time), now you can see what's really going on.

WRAP UPI hope you‘re much more comfortable using pointers. One of the most common uses for pointers is to process an incoming line on a serial port. Processing involves parsing out the different fields and performing appropriate operations. Typically, a pointer p (of type INT8) is set to the start of the line and advanced as characters are decoded.

Next time, I will look at a process for a complete design. I‘ll use UML to help capture and explain the design, and I'll work with the C language for the code. I‘ll tackle a CompactFlash interface design.

If you have requests or questions, let me know. I'll try to work them into a column.

Author‘s note: This is a continuation of my description of the C language and how it's used in embedded systems. If there are terms or concepts in this article that you don‘t understand, please refer to my previous articles about C language. The articles appear in the following issues of Circuit Cellar: 198, 200, 202, 204, 206, 208, 210,212, and 214. If there is anything else that you would like explained, please e-mail me and I will try to explain it in a column so that all of our readers may benefit.

George Martin (gmm50@att.net) began his career in the aerospace industry in 1969. After five years at a real job, he set out on his own and co-founded a design and manufacturing firm (www.embedded-designer.com)。 His designs typically include servo-motion control, graphical input and output, data acquisition,and remote control systems. George is a charter member of the Ciarcia Design Works Team. He‘s currently working on a mobile communications system that announces highway info. He is also a nationally ranked revolver shooter.

REFERENCE

[1] B. Kernighan and D. Ritchie, The C Programming Language (K&R),Prentice Hall PTR, Upper Saddle River, NJ, 1988.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值