LinuxQuestions.org
Help answer threads with 0 replies.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
LinkBack Search this Thread
Old 02-06-2012, 07:41 PM   #1
keithostertag
Member
 
Registered: Jul 2011
Location: Harrisburg, PA
Posts: 36

Rep: Reputation: Disabled
Beginning C programming- stuck on trivial program (compiler warnings)


Still working on learning about pointers and how to initialize data:-)

I've looked at this for hours and can't see the problem. Maybe I should move past it, but compiler warnings bug me...

Code:
#include <stdio.h>

int i;	               /* used as a counter */

struct records {
	char fname[30];
	char lname[30];
	char hphone[11];
	char email[52];
	} data[5] = { "Keith", "Ostertag", "7656952829", "keitho@stcktower.com",
		"Mary", "Sholey  ", "7657834872", "mstaopr@strktower.com",
		"Ann", "Couer  ", "6011234567", "arnoldk@republic.com",
		"Kerry", "Stamper", "4436667778", "whoknows@retired.com",
		"Jason", "Houseman", "5413234345", "angelface@stars.com"
		};

struct records *ptr;	/* Declare a pointer to type records */

int main(void)
{	                /* start of main */

ptr  = data             /* point to start of data */

for (i=0; i < 4; i++)
	{	        /* start of for loop */

printf("\n%d. %s %s\t%s\t%s", i, ptr->fname, ptr->lname, ptr->hphone, ptr->email);
ptr++;

	}	         /* end of for loop */

puts("");
return 0;

} /* end of main */
I get the following compile warnings:

tst.c:10:2: warning: missing braces around initializer [-Wmissing-braces]
tst.c:10:2: warning: (near initialization for ‘data[0]’) [-Wmissing-braces]

I realize there are lots of ways to do this, I just want to understand why the compiler is complaining and how to tweak it so the compiler doesn't complain.

Also a general question about posting these: should I include line numbers?

thanks,
Keith Ostertag
 
Click here to see the post LQ members have rated as the most helpful post in this thread.
Old 02-06-2012, 08:14 PM   #2
jb_gpk
Member
 
Registered: Dec 2010
Distribution: Debian
Posts: 30

Rep: Reputation: 13
it looks fine here.

Code:
$ gcc --version
gcc (Debian 4.4.5-8) 4.4.5
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc tsc.c 
$ ./a.out 

0. Keith Ostertag	7656952829	keitho@stcktower.com
1. Mary Sholey  	7657834872	mstaopr@strktower.com
2. Ann Couer  	6011234567	arnoldk@republic.com
3. Kerry Stamper	4436667778	whoknows@retired.com
the only error I found is at the line 23, you miss the semicolon at the end of the line:
Code:
ptr  = data             /* point to start of data */

Last edited by jb_gpk; 02-06-2012 at 08:17 PM.
 
Old 02-06-2012, 08:51 PM   #3
keithostertag
Member
 
Registered: Jul 2011
Location: Harrisburg, PA
Posts: 36

Original Poster
Rep: Reputation: Disabled
Hi-

Thanks for taking the time to look at my code. You have helped me by confirming that there are no obvious typos, other than line 23.

The missing ; in line 23 was actually a typo when copying over the code into the LQ form, thanks for catching that and sorry for not catching that in the post preview. I have it in the actual code that I compile.

Yes, the code does compile and does "work" in that it produces the expected output.

I'd like to know why I'm getting the compiler warnings. If you didn't get the same warnings that could be because I have a lot of compiler warning flags which run by default whenever I compile that you may not use. So, the code may actually be workable and not actually "wrong", but there is some reason that it doesn't suit the standard for the the "proper" way of doing it. For some reason the compiler thinks that I am missing a brace, evidently.

Keith
 
Old 02-06-2012, 09:10 PM   #4
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 361Reputation: 361Reputation: 361Reputation: 361
Quote:
tst.c:10:2: warning: missing braces around initializer [-Wmissing-braces]
tst.c:10:2: warning: (near initialization for ‘data[0]’) [-Wmissing-braces]
These are caused because your initializer works, but for the wrong reason.

Your data array is an array of structs. A struct initializer uses curly braces. An array initializer also uses curly braces. Therefore, you must nest your struct initializer within your array initializer:
Code:
} data[5] = { { "Keith", "Ostertag", "7656952829", "keitho@stcktower.com" },
	      { "Mary", "Sholey  ", "7657834872", "mstaopr@strktower.com" },
	      { "Ann", "Couer  ", "6011234567", "arnoldk@republic.com" },
	      { "Kerry", "Stamper", "4436667778", "whoknows@retired.com" },
	      { "Jason", "Houseman", "5413234345", "angelface@stars.com" }
		};
EDIT:
Your code is initializing an array. Each consecutive array location is given the appropriate value from your list of static strings. It just so happens that your struct layout maps one-to-one on top of those locations. Had your struct layout been different (e.g. using some non-32-bit data types), you may very well have encountered some problems with data being improperly placed within the structs.

Last edited by Dark_Helmet; 02-06-2012 at 09:15 PM.
 
2 members found this post helpful.
Old 02-06-2012, 09:34 PM   #5
keithostertag
Member
 
Registered: Jul 2011
Location: Harrisburg, PA
Posts: 36

Original Poster
Rep: Reputation: Disabled
Hi Dark-

Thanks much. Now it compiles without warnings.

As a newbie just starting with C programming, I was following an example in one of my textbooks. Which, as you now point out, is wrong. I'm waiting to get a copy of K&R, hopefully it will be better with this kind of basic stuff (even though dated).

I thought the original code could be correct since all the data between the braces would be stored sequentially. Is that not correct, even using multiple types? I thought it would work because the compiler keeps track of the sizes of the types when incrementing pointers.

Keith
 
Old 02-06-2012, 11:01 PM   #6
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 361Reputation: 361Reputation: 361Reputation: 361
Actually, I have to take back some of what I said. I had not looked at your struct definition closely. I only took the time to realize that you were declaring string. I immediately assumed you were using "char *" for each struct member as opposed to the literal "char []" declarations.

So, my explanation that your struct definition was one-to-one is incorrect.

What I would expect is happening is that the compiler realized you were missing the extra braces. The warning was then to let you know that you were relying on default of "assumed" behavior of the compiler--whereas you should be explicit.

Quote:
I thought it would work because the compiler keeps track of the sizes of the types when incrementing pointers.
It does--pointer arithmetic. However, understand that pointer arithmetic is top-level. Say you have this code:

Code:
#include <stdio.h>

typedef struct{
  char *name;
  unsigned age;
} personStruct;

int main( int argc, char *argv[] )
{
  personStruct peopleData[2] = { { "Lord Helmet", 123 },
                                 { "Colonel Sanders", 42 } };

  personStruct *targetPerson;
  char         *personName;

  targetPerson = peopleData;
  personName   = targetPerson->name;

  targetPerson += 1;
  personName   += 1;

  if( personName == targetPerson->name )
    printf( "personName moved from person 0 to person 1\n" );
  else
    printf( "personName did not move to person 1's name\n" );

  return 0;
}
That code should output "personName did not move to person 1's name"

When you add or subtract a value to a pointer, the address is adjusted by an amount equal to (value * sizeof(underlying data type of pointer)).

In this case, targetPerson points to a personStruct. A personStruct should have a size of 8: 4 bytes for the "char *" plus 4 bytes for the unsigned.

Similarly, personName points to a char. A char has a size of 1 byte.

So, when the "+= 1" statements occur, targetPerson is advanced by an absolute value of 8. The personName variable is advanced by an absolute value of 1. The targetPerson pointer does not cycle through its data members. Nor does personName automatically move to the next structure simply because it's a member of a larger struct.

The point of all of this is to say that pointer arithmetic does not look into the lower details of the underlying data structure.

Forgive me if I misunderstood your question.

Last edited by Dark_Helmet; 02-06-2012 at 11:03 PM.
 
Old 02-07-2012, 11:50 AM   #7
keithostertag
Member
 
Registered: Jul 2011
Location: Harrisburg, PA
Posts: 36

Original Poster
Rep: Reputation: Disabled
OK, but remember I am a beginner and am only now starting to study pointers. Part of what that means is that I haven't gotten very far into your code yet...

When I first ran your code I got the following compiler warnings:
Code:
dark-ori.c: In function ‘main’:
dark-ori.c:10:3: warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
dark-ori.c:11:34: warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
dark-ori.c:8:15: warning: unused parameter ‘argc’ [-Wunused-parameter]
dark-ori.c:8:27: warning: unused parameter ‘argv’ [-Wunused-parameter]
So I changed the main prototype to:
int main(void)
and changed line 4 from "char *name;" to "char name[];"

I then got the following compiler warnings:
Code:
dark.c:5:10: warning: ISO C90 does not support flexible array members [-pedantic]
dark.c:5:10: error: flexible array member not at end of struct
So I then changed line 4 to:
char name[30];
which compiles with no warnings, and produces the expected output.

Here is your code with my two changes:
Code:
#include <stdio.h>

typedef struct{
  char name[30];
  unsigned age;
} personStruct;

int main(void)
{
  personStruct peopleData[2] = { { "Lord Helmet", 123 },
                                 { "Colonel Sanders", 42 } };

  personStruct *targetPerson;
  char         *personName;

  targetPerson = peopleData;
  personName   = targetPerson->name;

  targetPerson += 1;
  personName   += 1;

  if( personName == targetPerson->name )
    printf( "personName moved from person 0 to person 1\n" );
  else
    printf( "personName did not move to person 1's name\n" );

  return 0;
}
I haven't studied parsing command line arguments yet, so I am too not concerned about the argc/*arv[] stuff at present. What I am most concerned with at this point are the details on how to correctly define, initialize, and use data types.

Thanks,
Keith
 
Old 02-07-2012, 12:02 PM   #8
MrCode
Member
 
Registered: Aug 2009
Location: Oregon, USA
Distribution: Arch
Posts: 859
Blog Entries: 31

Rep: Reputation: 144Reputation: 144
Pardon me if this is misguided, but is this not redundant?

Code:
struct records *ptr;	/* Declare a pointer to type records */

...

ptr  = data;             /* point to start of data */
(semicolon added for correctness)

As far as I can tell, you've already declared a "data" pointer in your structure definition above that; you should be able to use that pointer directly in your printf rather than declaring another struct pointer and assigning that to it. :-\
 
Old 02-07-2012, 12:19 PM   #9
keithostertag
Member
 
Registered: Jul 2011
Location: Harrisburg, PA
Posts: 36

Original Poster
Rep: Reputation: Disabled
Hi MrCode-

Not sure what you mean...

Code:
struct records *ptr;	/* Declare a pointer to type records */
is a declaration of a pointer type to a struct, and

Code:
ptr  = data;             /* point to start of data */
is an assignment of that pointer to a specific instance of the struct. At least that's how I am understanding it. How else would you determine which instance you wanted to point to?

Are you saying there is a simpler way to format the printf statement without assigning ptr=data ?

An example would be great.

Thanks,
keith
 
Old 02-07-2012, 12:44 PM   #10
MrCode
Member
 
Registered: Aug 2009
Location: Oregon, USA
Distribution: Arch
Posts: 859
Blog Entries: 31

Rep: Reputation: 144Reputation: 144
Quote:
Are you saying there is a simpler way to format the printf statement without assigning ptr=data ?

An example would be great.
My apologies…I thought perhaps that since you were declaring an array of "records" structs ("data[5]"), you could use that pointer directly, like so:

Code:
printf("\n%d. %s %s\t%s\t%s", i, data->fname, data->lname, data->hphone,data->email);

data++;
…but gcc has issues with that:

Code:
print-struct-contents.c:23:13: error: lvalue required as increment operand
Admittedly my experience dealing with structs and other such things is somewhat minimal, but I'm just trying to help…

Last edited by MrCode; 02-07-2012 at 12:46 PM.
 
Old 02-07-2012, 01:18 PM   #11
keithostertag
Member
 
Registered: Jul 2011
Location: Harrisburg, PA
Posts: 36

Original Poster
Rep: Reputation: Disabled
Hi MrCode-

Actually you are correct in the sense that the pointers are not needed. I am using code from a book intended to demonstrate the use of pointers, not simply to produce the output.

AFAIK the way I would format the printf statement without using pointers in the code at all is this:

Code:
printf("\n%d. %s %s\t%s\t%s", i, data[i].fname, data[i].lname, data[i].hphone, data[i].email);
AND, if you hadn't commented I wouldn't have figured that out simply because I had no provocation to do so.

So, thanks, you _are_ being helpful! I think it is awesome that I can get help with these trivial things just for the asking! I find it really difficult to learn programming without discussing the details with others.

Keith Ostertag
 
Old 02-07-2012, 02:17 PM   #12
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 361Reputation: 361Reputation: 361Reputation: 361
Quote:
Originally Posted by keithostertag View Post
OK, but remember I am a beginner and am only now starting to study pointers. Part of what that means is that I haven't gotten very far into your code yet...

When I first ran your code I got the following compiler warnings:
Code:
dark-ori.c: In function ‘main’:
dark-ori.c:10:3: warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
dark-ori.c:11:34: warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
dark-ori.c:8:15: warning: unused parameter ‘argc’ [-Wunused-parameter]
dark-ori.c:8:27: warning: unused parameter ‘argv’ [-Wunused-parameter]
To be honest, I cannot replicate all of your warning messages. The command I used to compile my sample code:
Code:
gcc -Wall -Wextra -o sample_code sample_code.c
I only receive the "unused parameter" messages. Those, as you correctly identified, are only for the command line arguments. I add the argc and argv variables to my main() declaration out of habit, and you can eliminate them (as you did) without affecting what the code was trying to demonstrate.

The other warnings can probably be eliminated by changing the struct definition to this:
Code:
typedef struct{
  const char *name;
  unsigned age;
} personStruct;
But again, because I can't replicate, I cannot know whether that will remove your warnings. What I believe the compiler is complaining about is that the string literals used for the names are created as string constants. As such, assigning their address to a non-const "char *" discards the const qualification from being a string literal--potentially leading to an attempt to modify the string contents using the "char *"--which would be illegal and result in a segfault/coredump/bad things.

EDIT:
As a side note, handling pointers (in general) is one of the more abstract topics and generally causes a lot grief for beginners. I'm happy to explain what I can, but just be prepared for what appear to be inexplicable errors once you start getting your hands dirty with them.

Last edited by Dark_Helmet; 02-07-2012 at 02:23 PM.
 
Old 02-07-2012, 02:40 PM   #13
keithostertag
Member
 
Registered: Jul 2011
Location: Harrisburg, PA
Posts: 36

Original Poster
Rep: Reputation: Disabled
To Dark_Helmet:

I've had some time today to look at your comment and code further. The distinction you are making about pointer arithmetic being "top-level" and that it "does not look into the lower details of the underlying data structure" now makes sense to me now, thanks very much for your explanation.

For confirmation and added practice I added printf statements to your code so I could see exactly what incrementing the pointers actually did. Here's the output:
Code:
Starting off targetPerson is = Lord Helmet
Starting off personName is = Lord Helmet
After incrementing personName is now = ord Helmet
After incrementing targetPerson is = Colonel Sanders
personName did not move to person 1's name
RE: the compiler warnings. Yes, I have a LOT of warning flags which I automatically invoke with the compiler- too many to list here :-))

I did as you suggested and changed that one line to "const char *name" and this is the warning I now get:
Code:
dark.c: In function ‘main’:
dark.c:17:16: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default]
I guess what confuses me is why you want to use "name" as a pointer to type char rather than as a string variable, which I do by using "char name[30]". I mean, don't you need to declare a size for it somewhere before initializing it? In order to claim memory space for it? Using the "const" type would seem inappropriate to me, Can you explain?

Thanks,
Keith

Keith
 
Old 02-07-2012, 02:58 PM   #14
MrCode
Member
 
Registered: Aug 2009
Location: Oregon, USA
Distribution: Arch
Posts: 859
Blog Entries: 31

Rep: Reputation: 144Reputation: 144
Quote:
Originally Posted by keithostertag
AFAIK the way I would format the printf statement without using pointers in the code at all is this:
Code:
printf("\n%d. %s %s\t%s\t%s", i, data[i].fname, data[i].lname, data[i].hphone, data[i].email);
…and you would be correct. Thanks for that.

Although, you technically are still using pointers, since the "[<index>]" notation is just shorthand for "*(<ptr> + <index>)".
 
Old 02-07-2012, 03:03 PM   #15
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 361Reputation: 361Reputation: 361Reputation: 361
Quote:
Originally Posted by keithostertag
I did as you suggested and changed that one line to "const char *name" and this is the warning I now get:
Code:
dark.c: In function ‘main’:
dark.c:17:16: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default]
Hah! The compiler is always smarter than me!

It's caused by roughly the same problem. Because the struct contains a "const char *" member, the "const" is discarded when we assign it to personName (which is declared as "char *").

Soooo, another slight modification--this time to the personName declaration:
Code:
const char   *personName

Quote:
I guess what confuses me is why you want to use "name" as a pointer to type char rather than as a string variable, which I do by using "char name[30]". I mean, don't you need to declare a size for it somewhere before initializing it? In order to claim memory space for it? Using the "const" type would seem inappropriate to me, Can you explain?
And so begins your descent into the belly of the pointer beast...

A few things to explain here.

First, when you use a string literal in a program (e.g. "Lord Helmet"), the compiler stores the string as part of the executable itself. Because the string is incorporated into the executable, it is protected--hence the automatic "const" qualification which prevents the executable from modifying the contents of the string. Additionally, whenever the code references "Lord Helmet", the compiler substitutes the address of where the string is stored. The address returned is a "const char *".

So, why is it a "const char *"? Because that's a C-style string. A string in C is any consecutive group of chars terminated by a NULL. A NULL is just a char with a value of 0. So, when you read "consecutive group of chars" it probably made you think "array." And that is precisely correct. In a sense, a "char *" does double-duty depending on how you interpret it. If the context of the code wants to get a single char value, then a "char *" is literally, a pointer to a single char. If, however, the context of the code uses the "char *" in the context of a string, the program will look at each consecutive char location until it reaches the NULL. That consecutive group of chars will be the value of the string.

Under normal circumstances, yes, a plain "char *" variable would need to have space allocated for it. You could do so with array syntax--which creates a fixed string length. Or you can use functions like malloc() which will reserve blocks of memory at run-time according to whatever space requirements you need. In the sample code I provided, calling malloc() was not necessary because I relied on the fact that the compiler placed the string in read-only portion of the executable. And because I did not intend to do any manipulation--only read--that was sufficient.

EDIT:
I should point out something that you may feel is an inconsistency in my explanation. In your original program, you used string literals in your initializer. Based on my explanation above, you would be rightly confused as to why your code did not generate the same "discards 'const'" warnings as mine did. By my explanation, those string literals should return "const char *" values, right? Well, they do. It's just that your struct declares your strings as fixed-length using the array syntax (and hence, reserves a block of memory for them). Because of that, the initializer realizes that it needs to copy the contents of the read-only string literals into your struct variables. Therefore, there is no "const" problem because you would be working on copies of the string literals--not the string literals themselves.

Last edited by Dark_Helmet; 02-07-2012 at 03:17 PM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
problem with compiler warnings rheosiva Linux - Kernel 4 06-05-2009 06:24 AM
LXer: Linux: Compiler Warnings LXer Syndicated Linux News 0 05-24-2007 09:01 PM
Beginning programming mikeymorgan Programming 38 06-28-2005 07:59 AM
just beginning c programming mcd Programming 12 02-24-2005 05:06 PM
java compiler warnings exodist Programming 2 05-09-2004 01:42 AM


All times are GMT -5. The time now is 11:19 AM.

Main Menu
 
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: @linuxquestions
Open Source Consulting | Domain Registration