Monday, May 5, 2008

C++ const pointer declaration

Came across this interesting issue with the use of C++ const pointers, that help explain the effects of using the const keyword, incorrectly with pointers.
Here's the minimum code needed to understand this,

//File: a.h
#ifndef __MY_HEADER__
#define __MY_HEADER__
const char* ptr = "A String";
#endif


//File: a.cpp
#include "a.h"
int main()
{
return 0;
}


//File: b.cpp
#include "a.h"

Compiling the above code with g++ (used version 2.8.1) gave the following error,
/var/tmp/ccp3aGWX2.o(.data+0x0): multiple definition of `ptr'
/var/tmp/ccp3aGWX1.o(.data+0x0): first defined here


At first glance this seemed like a weird error from the linker. After all this was just a constant string in the header file (or wasn't it?). But the linker was indicating that the variable was in the .data section of the object file, which was not what was wanted or expected. And on replacing this pointer with an array it worked fine.

To confirm I objdump'ed the files.
The one with the 'const char *' showed the following,
a.o: file format elf32-sparcb.o: file format elf32-sparc
SYMBOL TABLE:

00000000 l df *ABS* 00000000 a.cpp
00000000 l d .text 00000000
00000000 l d .data 00000000
00000000 l d .bss 00000000
00000000 l .text 00000000 gcc2_compiled.
00000000 l d .rodata 00000000
00000000 l d .eh_frame 00000000
00000000 l .eh_frame 00000000 __FRAME_BEGIN__
00000000 l *ABS* 00000000 *ABS*
00000000 l d .comment 00000000
00000000 g O .data 00000004 ptr
00000000 g F .text 00000024 main
SYMBOL TABLE:

00000000 l df *ABS* 00000000 b.cpp
00000000 l d .text 00000000
00000000 l d .data 00000000
00000000 l d .bss 00000000
00000000 l .text 00000000 gcc2_compiled.
00000000 l d .rodata 00000000
00000000 l d .eh_frame 00000000
00000000 l .eh_frame 00000000 __FRAME_BEGIN__
00000000 l d .comment 00000000
00000000 g O .data 00000004 ptr

While the objdump of the files with the const char array showed this,
a.o: file format elf32-sparcb.o: file format elf32-sparc
SYMBOL TABLE:

00000000 l df *ABS* 00000000 a.cpp
00000000 l d .text 00000000
00000000 l d .data 00000000
00000000 l d .bss 00000000
00000000 l .text 00000000 gcc2_compiled.
00000000 l d .rodata 00000000
00000000 l O .rodata 00000009 ptr
00000000 l d .eh_frame 00000000
00000000 l .eh_frame 00000000 __FRAME_BEGIN__
00000000 l *ABS* 00000000 *ABS*
00000000 l d .comment 00000000
00000000 g F .text 00000024 main
SYMBOL TABLE:

00000000 l df *ABS* 00000000 b.cpp
00000000 l d .text 00000000
00000000 l d .data 00000000
00000000 l d .bss 00000000
00000000 l .text 00000000 gcc2_compiled.
00000000 l d .rodata 00000000
00000000 l O .rodata 00000009 ptr
00000000 l d .eh_frame 00000000
00000000 l .eh_frame 00000000 __FRAME_BEGIN__
00000000 l d .comment 00000000

This showed that const char *ptr = "A string" was clearly not a const to the compiler (it was being put into the .data section and not the .rodata), leading me to look more closely at what I was declaring. What I had was a variable pointer to a const string which was causing the linker to complain.

The const declaration in C++ applies to whatever is on its immediate left (except when there is nothing to its left, in which case it then applies to whatever is to its immediate right). So in the above declaration the const applied to the char not to the '*'(pointer) and thus declaring a string that cannot be modified ("A String"), with a pointer (ptr) pointing to it (duh!) that could potentially be made to point to something else.

This left the gcc linker (ld) uncomfortable and caused it to complain (with diab however it just issued a warning and continued to link).

Changing the declaration to
const char * const ptr = "A string";
resolved the issue and thought it taught me to be more careful with my declaration of const's, I think in this case at least it would have been better to have used the string type instead. :)

No comments: