paulsm4 hits the nail on the head.
What follows isn't precisely what
charlitos was asking about, but there are ways you can get the compiler to help you avoid difficulty.
Segment violation is fine and dandy, but you can save yourself some potential embarrassment in the real world by getting the compiler to catch as many bugs as it can. All you have to do is know how to use
const.
Here's a shell script I just ran. The meat of it is a C program. I'm showing the script so you can replicate it, but part of what the script does is to display the C program with line numbers, so you can follow the error messages.
(Line numbers for the whole script wouldn't be quite accurate for finding errors in the C program, and we need to know exactly which line number the compiler finds offensive.)
Anyway, here's the script:
Code:
#!/bin/bash
cat > 1.c <<EOD
#include <stdio.h>
#if DECLARATION==1
void yellow_subroutine( char * parameter)
#endif
#if DECLARATION==2
void yellow_subroutine( char * const parameter)
#endif
#if DECLARATION==3
void yellow_subroutine( char const * parameter)
#endif
#if DECLARATION==4
void yellow_subroutine(const char * parameter)
#endif
{
#if DEFINITION==1
parameter[1]='o';
#endif
#if DEFINITION==2
parameter++;
parameter[0]='o';
#endif
} /* yellow_subroutine() */
int main(void)
{
char broiled[]="tick";
yellow_subroutine(broiled);
broiled[1]='o';
printf("%s\n",broiled);
return 0;
} /* main() */
EOD
cat -n 1.c
for DECLARATION in 1 2 3 4
do
for DEFINITION in 1 2
do
echo DECLARATION is $DECLARATION\; DEFINITION is $DEFINITION
gcc -DDECLARATION=$DECLARATION -DDEFINITION=$DEFINITION -Wall 1.c -o 1
if [ $? = 0 ]
then
1
fi
done
done
What the script does this:
- list the source code with line numbers; and
- in eight different ways, compile the code and, if the compilation is successful, run the code.
Let's look at the program, piece by piece.
Function
main() does this:
- declare the "constant" array;
- call function yellow_subroutine();
- display the "constant" array; and
- exit.
Now here's where it gets a little interesting. Function
yellow_subroutine() is declared in one of four ways, depending on how the program is compiled.
All those
consts seem rather confusing, but the rules are simple:
- What is to the left of the const is the type of what is constant.
- What is to the right of the const is the thing that is constant.
The first declaration has no
const, so we'll skip over that.
Here's the second declaration:
Code:
void yellow_subroutine( char * const parameter)
To the left is
char *, which is pointer to
char. To the right is
parameter, which is (surprise!) of that type. So it's the pointer that is constant. With this declaration, one can change the characters themselves, but
not the pointer that points to them.
And later, we'll see that this is exactly what happens.
Here's the third declaration:
Code:
void yellow_subroutine( char const * parameter)
To the left is
char, which is the actual character(s). To the right is
*parameter, which is (again, surprise!) of that type:
*parameter is a character.
With this declaration, one
cannot change the characters themselves, but one
can change the pointer that points to them.
And again, we'll see that this is exactly what happens.
But what about the fourth declaration? This is what we so often see in code:
Code:
void yellow_subroutine(const char * parameter)
The rules break down here. In your mind, you have to slide that
const to the right of the next token, and then apply the rules.
Really, there's no reason for the compiler to have to allow this fourth form, but they dumb things down for us so we can just get on with our day jobs.
A brief comment on these rules before we look at the results. You can have more than one
const in the declaration if you wish, like this:
Code:
void yellow_subroutine( char const * const parameter)
I urge you to try that variation to show yourself that neither the pointer nor the characters will be changeable.
You can even (if your situation demands) sprinkle more asterisks in there, having absolute control over what is constant and what is not:
Code:
void yellow_subroutine(char const ** const *** const * const * parameter)
But for this experiment, we'll just stick with the first four forms.
Now let's move on to the definition of that function: what the function actually does. In the first definition, we have:
This line (line 17) doesn't change the pointer, only the data.
In the second definition, we have:
Code:
parameter++;
parameter[0]='o';
Line 20 changes the pointer, and then line 21 changes the data.
If neither the data nor the pointer is declared
const, then both definitions change the same character.
Ok, let's see what happened.
When I ran this script, I got this output:
Code:
1 #include <stdio.h>
2
3 #if DECLARATION==1
4 void yellow_subroutine( char * parameter)
5 #endif
6 #if DECLARATION==2
7 void yellow_subroutine( char * const parameter)
8 #endif
9 #if DECLARATION==3
10 void yellow_subroutine( char const * parameter)
11 #endif
12 #if DECLARATION==4
13 void yellow_subroutine(const char * parameter)
14 #endif
15 {
16 #if DEFINITION==1
17 parameter[1]='o';
18 #endif
19 #if DEFINITION==2
20 parameter++;
21 parameter[0]='o';
22 #endif
23
24 } /* yellow_subroutine() */
25
26 int main(void)
27 {
28 char broiled[]="tick";
29
30 yellow_subroutine(broiled);
31
32 printf("%s\n",broiled);
33
34 return 0;
35
36 } /* main() */
DECLARATION is 1; DEFINITION is 1
tock
DECLARATION is 1; DEFINITION is 2
tock
DECLARATION is 2; DEFINITION is 1
tock
DECLARATION is 2; DEFINITION is 2
1.c: In function 'yellow_subroutine':
1.c:20: error: increment of read-only location
DECLARATION is 3; DEFINITION is 1
1.c: In function 'yellow_subroutine':
1.c:17: error: assignment of read-only location
DECLARATION is 3; DEFINITION is 2
1.c: In function 'yellow_subroutine':
1.c:21: error: assignment of read-only location
DECLARATION is 4; DEFINITION is 1
1.c: In function 'yellow_subroutine':
1.c:17: error: assignment of read-only location
DECLARATION is 4; DEFINITION is 2
1.c: In function 'yellow_subroutine':
1.c:21: error: assignment of read-only location
I would first note quickly in passing that just as one would expect, the output for declaration 4:
Code:
void yellow_subroutine(const char * parameter)
is exactly the same as for declaration 3:
Code:
void yellow_subroutine( char const * parameter)]
So we won't worry about declaration 4 any more.
For declaration 1, which was this:
Code:
void yellow_subroutine( char * parameter)
everything "worked":
Code:
DECLARATION is 1; DEFINITION is 1
tock
DECLARATION is 1; DEFINITION is 2
tock
We would expect that, because there was no
const in the way.
For declaration 2, which forbade changing the pointer but allowed changing the characters:
Code:
void yellow_subroutine( char * const parameter)
we got this:
Code:
DECLARATION is 2; DEFINITION is 1
tock
DECLARATION is 2; DEFINITION is 2
1.c: In function 'yellow_subroutine':
1.c:20: error: increment of read-only location
Definition 1 doesn't try to change the pointer. Definition 2 changes the pointer on line 20, and the data on line 21.
For declaration 3, which forbade changing the data but allowed changing the pointer:
Code:
void yellow_subroutine( char const * parameter)
we got this:
Code:
DECLARATION is 3; DEFINITION is 1
1.c: In function 'yellow_subroutine':
1.c:17: error: assignment of read-only location
DECLARATION is 3; DEFINITION is 2
1.c: In function 'yellow_subroutine':
1.c:21: error: assignment of read-only location
Definition 1 changes the data on line 17, and definition 2 changes the data on line 21. Both fail at compile time. But definition 2 also changes the pointer on line 20, and this works.