Copyright 2004 by M. Uli Kusterer Tue, 30 Nov -1901 08:00:00 GMT Comments on article book9 at Zathras.de http://www.zathras.de/angelweb/book9.htm book9 Comments witness_dot_of_dot_teachtext_at_gmx_dot_net (M. Uli Kusterer) witness_dot_of_dot_teachtext_at_gmx_dot_net (M. Uli Kusterer) en-us Comment 29 by Withan http://masters-of-the-void.com/book9.htm#comment29 http://masters-of-the-void.com/book9.htm#comment29 You're right. How do I even get rid of it? Never mind, I right clicked it (little blue arrow thingy) and it allowed me to disable or delete it. Nice functionality in the Xcode environment.

Does anyone use any other editors to write C for the Apple platforms?
Comment 28 by Uli Kusterer http://masters-of-the-void.com/book9.htm#comment28 http://masters-of-the-void.com/book9.htm#comment28 Uli Kusterer writes:
Withan, I just tried to run this program, and it works just fine for me. And reading over the code, it looks just fine as well. The warnings aren't important, they're just about style and don't influence what our programs do (the next version of this tutorial will get rid of them, but it requires a bit of restructuring of the whole tutorial).

You say it stops at the DoCleanUp() line. Is there any error message at the right of the line? Is there a little icon at the left of the line (like a little arrow, in addition to the little yellow warning icon from the prototype error)? You may have accidentally set a breakpoint (see Chapter 13). What happens if you click the little "continue program execution" right-facing arrow at the bottom of the window? (Not the "Run" play button at the top).
Comment 27 by Withan http://masters-of-the-void.com/book9.htm#comment27 http://masters-of-the-void.com/book9.htm#comment27 Full text of code from previous comment



//
// main.c
// MyFirstProgram
//
// Created by Withan Lemmon on 11/17/11.
// Copyright (c) 2011 Advanta-STAR Automotive Research. All rights reserved.
//

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>



// Data Structure:
struct CDDatabaseEntry
{
char artist[40];
char composer[40];
char albumName[40];
int trackCount;
bool isSampler;
};


//Global Variables

int gNumDatabaseEntries = 0;
struct CDDatabaseEntry* gDatabase = NULL;



//New Subroutines


void DoNewCommand()
{
char yesOrNo;
// First, create a new array element (or a new array if we don't have one, yet.
if( gDatabase == NULL)
{
gDatabase = malloc(sizeof(struct CDDatabaseEntry) ); //size of 1 element
if( gDatabase == NULL)
{
printf("Error: Counldn't create a new entry!n" );
return;

}
}
else
{
struct CDDatabaseEntry* newPtr = NULL;
newPtr = realloc( gDatabase,(gNumDatabaseEntries + 1) *sizeof(struct CDDatabaseEntry) );
if(newPtr == NULL) //Oh uh!
{
//We just keep the old pointer
printf( "ERROR: Couldn't create a new entry!n" );
return;

}
//newPtr is our new ptr, gDatabase is no longer valid
gDatabase = newPtr; //Remember newPtr in gDatabase.

}
//Remember we have one more entry
gNumDatabaseEntries += 1;


//now replace garbage data in latest entry with user input
printf("Artist Name: ");
scanf("%39s",gDatabase[gNumDatabaseEntries - 1 ].artist);
fpurge(stdin);

printf("Composer: ");
scanf("%39s",gDatabase[gNumDatabaseEntries - 1 ].composer);
fpurge(stdin);

printf("Album Name: ");
scanf("%39s",gDatabase[gNumDatabaseEntries - 1 ].albumName);
fpurge(stdin);

printf("Number of Tracks: ");
scanf("%d",&gDatabase[gNumDatabaseEntries - 1 ].trackCount);
fpurge(stdin);

printf("Sampler? (y/n): ");
scanf("%c",&yesOrNo);
fpurge(stdin);

gDatabase[gNumDatabaseEntries - 1 ].isSampler = (yesOrNo == 'y' || yesOrNo == 'Y');


}


void DoListCommand()

{
int x = 0;

if(gDatabase == NULL)
{
printf("There are no CDs in the database.n");
return;

}
while (x < gNumDatabaseEntries )
{
printf("Artist Name: %39sn",gDatabase[x].artist);
printf("Composer: %39sn",gDatabase[x].composer);
printf("Album Name: %39sn",gDatabase[x].albumName);
printf("Number of Tracks: %dn",gDatabase[x].trackCount);
if( gDatabase[x].isSampler)
{
printf("This CD is a sampler.n");
}
printf("n");

x += 1;
}

}


void DoCleanUp( void )
{

if(gDatabase != NULL) //We have allocated memory, i.e. added at least 1 record
{
free (gDatabase);
gDatabase = NULL;
gNumDatabaseEntries = 0;
}

}

//Main event loop, fetches user input and calls our DoXXX functions to do the work

int main ()
{

bool keepRunning = true;
char userInput[11];

while (keepRunning == true )

{
printf("Type NEW, LIST, or QUIT:n> " );
scanf( "%10s", userInput);
fpurge(stdin);

if ( (strcasecmp(userInput,"NEW") == 0) || (strcasecmp(userInput,"N") == 0) )
DoNewCommand();
else if( ( strcasecmp( userInput, "LIST" ) == 0 ) || ( strcasecmp( userInput, "L" ) == 0 ))
DoListCommand();
else if(( strcasecmp( userInput, "QUIT" ) == 0 ) ||( strcasecmp( userInput, "Q" ) == 0 ))
{
printf( "We're done." );
keepRunning = false; // We're finished.
}
else
printf( "ERROR: Unknown command "%s"!nn", userInput );

}

DoCleanUp();

return 0;
}
Comment 26 by Withan http://masters-of-the-void.com/book9.htm#comment26 http://masters-of-the-void.com/book9.htm#comment26 It runs fine all the way, except when I quit. It stops on the DoCleanUp() line, without going to the subroutine, it seems. I've tried defining the subroutine "void DoCleanUp()" and "void DoCleanUp(void)"

I also tried moving the DoCLeanUp subroutine to the very top (after the global variables. My Xcode 4.2 compiler is giving warnings at the initiation of each subroutine: "No previous prototype of function 'DoCleanUp'" "No previous prototype of function 'DoListCommand'" "No previous prototype of function 'DoNewCommand'"
Comment 25 by Withan http://masters-of-the-void.com/book9.htm#comment25 http://masters-of-the-void.com/book9.htm#comment25 It runs fine all the way, except when I quit. It stops on the DoCleanUp() line, without going to the subroutine, it seems. I've tried defining the subroutine "void DoCleanUp()" and "void DoCleanUp(void)"

I also tried moving the DoCLeanUp subroutine to the very top (after the global variables. My Xcode 4.2 compiler is giving warnings at the initiation of each subroutine: "No previous prototype of function 'DoCleanUp'" "No previous prototype of function 'DoListCommand'" "No previous prototype of function 'DoNewCommand'"
Comment 24 by Uli Kusterer http://masters-of-the-void.com/book9.htm#comment24 http://masters-of-the-void.com/book9.htm#comment24 Uli Kusterer writes:
Denis, you wrote all other functions inside the DoNewCommand() function's brackets for one. That won't work.

The "No Previous Prototype" warning shouldn't happen, but it may be that the defaults for Xcode 4.1 are different from those for Xcode 3. If it still occurs after you've moved the other functions out of DoNewCommand(), skip ahead to chapter 11 "Organizing your Code" and add declarations for all your functions.

It isn't strictly necessary, but these days Xcode likes to enforce good style.
Comment 23 by Denis http://masters-of-the-void.com/book9.htm#comment23 http://masters-of-the-void.com/book9.htm#comment23 #include <stdio.h>
Uli,
Great tutorial. Have three error messages I cannot reconcile, they are included as comments in the code below. I'm using xCode 4.1 on a brand new Mac mini with OS Lion.

#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

// Data structure:

struct CDDatabaseEntry
{
char artist[40];
char composer[40];
char albumName[40];
int trackCount;
bool isSampler;
};

//Global variables:
int gNumDatabaseEntries = 0;
struct CDDatabaseEntry* gDatabase = NULL;

//Main event loop:
//Fetches user input and calls our DoXXX functions to do the work.

void DoNewCommand() //GOT ERROR HERE "no previous prototype for function DoNewCommand..."
{
char yesOrNo;

//First, create a new array element (or a new array if we don't have one yet):
if ( gDatabase == NULL )
{
gDatabase = malloc( sizeof(struct CDDatabaseEntry) ); //size of one element.
if( gDatabase == NULL ) // Still NULL? malloc() must have returned NULL due to error.
{
printf( "ERROR: Couldn't create a new entry!n");
return;
}
}
else
{
struct CDDatabaseEntry* newPtr = NULL;
newPtr = realloc( gDatabase, (gNumDatabaseEntries +1) *sizeof(struct CDDatabaseEntry));
if( newPtr == NULL ) //Error! Out of memory?
{
//We just keep the old pointer in gDatabase.
gDatabase = newPtr; //Remember newPtr in gDatabase.
}
//Make sure we remember we have one more entry:

gNumDatabaseEntries += 1;

//Now replace the garbage data in the new, last entry with data the user entered.

printf( "Artist Name: ");
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].artist );
fpurge(stdin);

printf( "Composer: ");
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].composer );
fpurge(stdin);

printf( "Album Name: ");
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].albumName );
fpurge(stdin);

printf( "No. of Tracks: ");
scanf( "%d", gDatabase[ gNumDatabaseEntries -1 ].trackCount );
fpurge(stdin);

printf( "Sampler? (y/n): ");
scanf( "%c", &yesOrNo );
fpurge(stdin);

gDatabase[ gNumDatabaseEntries -1 ].isSampler = (yesOrNo == 'y' || yesOrNo == 'Y');
}
void DoListCommand() //ERROR HERE, wants a semicolon at end of this line?
{
int x = 0;

if(gDatabase == NULL )
{
printf("There are no CDs in the database.n");
return;
}

while ( x < gNumDatabaseEntries )
{
printf( "Artist Name: %sn", gDatabase[ x].artist);
printf( "Composer: %sn", gDatabase[ x].composer);
printf( "Album Name: %sn", gDatabase[ x].albumName);
printf( "No. of Tracks: %dn", gDatabase[ x].trackCount);
if( gDatabase[ x ].isSampler )
printf("tThis CD is a sampler.n");
printf( "n" ); //Add an empty line for spae to the next CD.
x += 1;
}
}
void DoCleanUp( void );
{
if(gDatabase != NULL ) //We have allocated memory?
{
free( gDatabase );
gDatabase = NULL; //Not really necessary, but good style.
gNumDatabaseEntries = 0;
}
}
int main ();
{
bool keepRunning = true;
char userInput[11];

while ( keepRunning == true )
{
printf( "Type NEW , LIST, or QUIT:n " );
scanf( "%10s", userInput );
fpurge( stdin );

if( strcmp( userInput, "NEW" ) == 0 )
printf( "I'll write the code for NEW later...nn" );
else if( strcmp( userInput, "LIST" ) == 0)
printf( "I'll write the code for LIST later...nn" );
else if( strcmp( userInput, "QUIT" ) == 0 )
keepRunning = false; //we're finished.
else
printf( "ERROR: Unknown command "%s"!nn", userInput );
}

DoCleanUp(); //ERROR "implicit declaration of function DoCleanUp is invalid in C99"
}
return 0; //Void function DoNewCommand should not return a value.
}
Comment 22 by Andre http://masters-of-the-void.com/book9.htm#comment22 http://masters-of-the-void.com/book9.htm#comment22 Hey Uli,

thanks a lot for the tutorial. I am refreshing my C and especially memory management is important to me. I wanted to say that and suggest you to introduce flattr to your tutorial.* I'd be glad to flattr you and I am sure that there are more that would.

Thanks again!


*Disclaimer: I am not affiliated with flattr, but I use it and I like the idea a lot.
Comment 21 by Uli Kusterer http://masters-of-the-void.com/book9.htm#comment21 http://masters-of-the-void.com/book9.htm#comment21 Uli Kusterer writes:
Jacob, see my earlier reply to stijnschoor. :-)
Comment 20 by Jacob Hallman http://masters-of-the-void.com/book9.htm#comment20 http://masters-of-the-void.com/book9.htm#comment20 Hey Uli,

I'm loving the tutorials, kind of makes me wish I'd majored in CS (graduating with a Philosophy degree in May). That said, I really like the Database program and was wondering how I would go about getting the data to persist from one session to the next?

I suspect it has something to do with writing and calling a file of some sort, and thus incurs a whole new level of complexity. Could you just point me in the right direction?

Thanks again for the help.
Comment 19 by Uli Kusterer http://masters-of-the-void.com/book9.htm#comment19 http://masters-of-the-void.com/book9.htm#comment19 Uli Kusterer writes:
Alex,

you're declaring the gender as an array of 2 ints. You want 1 int. Also, the scanf statement takes a pointer, so you'll have to write &gDatabase[ gNumDatabaseEntries -1 ].gender there (since an array of 2 ints is a pointer, it works for you for now, but when you printf it, you print the address of the array. To get the right value with the array, you'd have to write gDatabase[ x ].gender[0] in your printf. But really, you want one int, not an array of two ints.
Comment 18 by Alex http://masters-of-the-void.com/book9.htm#comment18 http://masters-of-the-void.com/book9.htm#comment18 Hey, wondering if I could get some help with this..

I've modified this project into an employee database kind of thing, where the user is prompted for their name, email, gender, and whether they are employed or not. If they are employed, they are asked where and at what position. It works well, with the exception that I can't seem to figure out the syntax for displaying the gender in the List command. Here's what I've got:

struct CDDatabaseEntry
{
char firstName[40];
char lastName[40];
char email[40];
char employer[40];
char position[40];
int gender[2];
bool employed;
};


//All the usual things in betwee, works fine.

//Within the Main command:

printf("Gender (1/2): ");
scanf("%d", gDatabase[ gNumDatabaseEntries -1 ].gender);
fpurge( stdin );

//And here's my List command:

void DoListCommand()
{
int x = 0;

if( gDatabase == NULL )
{
printf("There are no employees in the database.n");
return;
}

while( x < gNumDatabaseEntries )
{
printf( "First Name: %sn", gDatabase[ x ].firstName );
printf( "Last Name: %sn", gDatabase[ x ].lastName );
printf( "Email Address: %sn", gDatabase[ x ].email );
if( gDatabase[ x ].gender == 1)
printf( "tThis person is a man.n" );
else {
printf("tThis person is a woman.n");
}

printf("Gender: %dn.", gDatabase[ x ].gender);

printf( "n" ); // Add an empty line for space to the next CD.

x += 1;
}}

The problem is in my syntax for the IF command of gender; I'm getting a "comparison between pointer and integer" warning, and upon running the program, the gender integer seems to be in the 10,000s, which obviously isn't right.

Any pointers? Thanks.
Comment 17 by Isaac http://masters-of-the-void.com/book9.htm#comment17 http://masters-of-the-void.com/book9.htm#comment17 @ phill, you've constructed your "DoNewCommand()" and other definitions inside the main() function. They should be outside and before the main() function for them to be recognized when called.

I've been taking a crack at extending the code and creating an "EDIT" function to edit the existing elements inside one data structure, to challenge my new knowledge. But I noticed that to "identify" one new structure, you simply add 1 to the existing "gNumDatabaseEntries" to offset the count when listing. That makes it difficult to search for individual data structures by name since they don't each have a unique identity.

My question is (since I'm still new to all this), am I seeing this correctly? Is there a simpler alternative with the existing code, or do I have to rewrite it so that it assigns each new data structure with it's own identity, thus having to rewrite the "LIST" function as a byproduct?
Comment 16 by José http://masters-of-the-void.com/book9.htm#comment16 http://masters-of-the-void.com/book9.htm#comment16 Finally I had my aha moment.
Thanks so much to the writer of this tutorial.
Comment 15 by phill http://masters-of-the-void.com/book9.htm#comment15 http://masters-of-the-void.com/book9.htm#comment15 I get a error when I try to build the code that says "expected expression before 'void'"

here is my code

#include <stdio.h> // declares printf(), scanf(), and fpurge()
#include <stdbool.h> //declares bool
#include <string.h> // declares strcmp()
#include <stdlib.h> // We'll need that later for malloc() and realloc()

// Data structure
struct CDDatabaseEntry
{
char artist[40];
char composer[40];
char albumName[40];
int trackCount;
bool isSampler;
};

//Global variables:
int gNumDatabaseEntries = 0;
struct CDDatabaseEntry* gDatabase = NULL;

// Main event loop:
//Fetches user input and calls our DoXXX functions to do the work.
int main()
{
bool keepRunning = true;
char userInput[11];

while ( keepRunning == true )
{
printf( "Type NEW, LIST, or QUIT:\n" );
scanf( "%10s", userInput );
fpurge( stdin );

if ( strcmp( userInput, "NEW" ) == 0 )



void DoNewCommand()
{
char yesOrNo;

// First, create a new array element (or a new array if we don't have one yet):
if( gDatabase == NULL )
{
gDatabase = malloc( sizeof(struct CDDatabaseEntry) ); // size of 1 element.
if( gDatabase == NULL ) // Still NULL? malloc() must have returned NULL due to error.
{
printf( "ERROR: Couldn't create a new entry!\n" );
return;
}
}
else
{
struct CDDatabaseEntry* newPtr = NULL;
newPtr = realloc( gDatabase, (gNumDatabaseEntries +1) *sizeof(struct CDDatabaseEntry) );
if( newPtr == NULL ) // Error! Out of memory?
{
// We just keep the old pointer in gDatabase.
printf( "ERROR: Couldn't create a new entry!\n" );
return;
}
// newPtr is our new ptr, gDatabase is no longer valid!
gDatabase = newPtr; // Remember newPtr in gDatabase.
}

// Make sure we remember we have one more entry:
gNumDatabaseEntries += 1;

// Now replace the garbage data in the new, last entry with data the user entered:
printf( "Artist Name: " );
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].artist );
fpurge( stdin );

printf( "Composer: " );
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].composer );
fpurge( stdin );

printf( "Album Name: " );
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].albumName );
fpurge( stdin );

printf( "No. of Tracks: " );
scanf( "%d", &gDatabase[ gNumDatabaseEntries -1 ].trackCount );
fpurge( stdin );

printf( "Sampler? (y/n): " );
scanf( "%c", &yesOrNo );
fpurge( stdin );

gDatabase[ gNumDatabaseEntries -1 ].isSampler = (yesOrNo == 'y' || yesOrNo == 'Y');
}
else if( strcmp( userInput, "LIST" ) == 0)
void DoListCommand()
{
int x = 0;

if( gDatabase == NULL )
{
printf("There are no CDs in the database.\n");
return;
}

while( x < gNumDatabaseEntries )
{
printf( "Artist Name: %s\n", gDatabase[ x ].artist );
printf( "Composer: %s\n", gDatabase[ x ].composer );
printf( "Album Name: %s\n", gDatabase[ x ].albumName );
printf( "No. of Tracks: %d\n", gDatabase[ x ].trackCount );
if( gDatabase[ x ].isSampler )
printf( "\tThis CD is a sampler.\n" );
printf( "\n" ); // Add an empty line for space to the next CD.

x += 1;
}
}
else if( strcmp( userInput, "QUIT" ) ==0)
keepRunning = false; // we're finished
else
printf("ERROR: Unknown command \"%s\"!\n\n", userInput );
}

void DoCleanUp( void )
{
if( gDatabase != NULL ) // We have allocated memory?
{
free( gDatabase );
gDatabase = NULL; // Not really necessary, but good style.
gNumDatabaseEntries = 0;
}
}
Comment 14 by phill http://masters-of-the-void.com/book9.htm#comment14 http://masters-of-the-void.com/book9.htm#comment14 I get a error when I try to build the code that says "expected expression before 'void'"

here is my code

#include <stdio.h> // declares printf(), scanf(), and fpurge()
#include <stdbool.h> //declares bool
#include <string.h> // declares strcmp()
#include <stdlib.h> // We'll need that later for malloc() and realloc()

// Data structure
struct CDDatabaseEntry
{
char artist[40];
char composer[40];
char albumName[40];
int trackCount;
bool isSampler;
};

//Global variables:
int gNumDatabaseEntries = 0;
struct CDDatabaseEntry* gDatabase = NULL;

// Main event loop:
//Fetches user input and calls our DoXXX functions to do the work.
int main()
{
bool keepRunning = true;
char userInput[11];

while ( keepRunning == true )
{
printf( "Type NEW, LIST, or QUIT:\n" );
scanf( "%10s", userInput );
fpurge( stdin );

if ( strcmp( userInput, "NEW" ) == 0 )



void DoNewCommand()
{
char yesOrNo;

// First, create a new array element (or a new array if we don't have one yet):
if( gDatabase == NULL )
{
gDatabase = malloc( sizeof(struct CDDatabaseEntry) ); // size of 1 element.
if( gDatabase == NULL ) // Still NULL? malloc() must have returned NULL due to error.
{
printf( "ERROR: Couldn't create a new entry!\n" );
return;
}
}
else
{
struct CDDatabaseEntry* newPtr = NULL;
newPtr = realloc( gDatabase, (gNumDatabaseEntries +1) *sizeof(struct CDDatabaseEntry) );
if( newPtr == NULL ) // Error! Out of memory?
{
// We just keep the old pointer in gDatabase.
printf( "ERROR: Couldn't create a new entry!\n" );
return;
}
// newPtr is our new ptr, gDatabase is no longer valid!
gDatabase = newPtr; // Remember newPtr in gDatabase.
}

// Make sure we remember we have one more entry:
gNumDatabaseEntries += 1;

// Now replace the garbage data in the new, last entry with data the user entered:
printf( "Artist Name: " );
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].artist );
fpurge( stdin );

printf( "Composer: " );
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].composer );
fpurge( stdin );

printf( "Album Name: " );
scanf( "%39s", gDatabase[ gNumDatabaseEntries -1 ].albumName );
fpurge( stdin );

printf( "No. of Tracks: " );
scanf( "%d", &gDatabase[ gNumDatabaseEntries -1 ].trackCount );
fpurge( stdin );

printf( "Sampler? (y/n): " );
scanf( "%c", &yesOrNo );
fpurge( stdin );

gDatabase[ gNumDatabaseEntries -1 ].isSampler = (yesOrNo == 'y' || yesOrNo == 'Y');
}
else if( strcmp( userInput, "LIST" ) == 0)
void DoListCommand()
{
int x = 0;

if( gDatabase == NULL )
{
printf("There are no CDs in the database.\n");
return;
}

while( x < gNumDatabaseEntries )
{
printf( "Artist Name: %s\n", gDatabase[ x ].artist );
printf( "Composer: %s\n", gDatabase[ x ].composer );
printf( "Album Name: %s\n", gDatabase[ x ].albumName );
printf( "No. of Tracks: %d\n", gDatabase[ x ].trackCount );
if( gDatabase[ x ].isSampler )
printf( "\tThis CD is a sampler.\n" );
printf( "\n" ); // Add an empty line for space to the next CD.

x += 1;
}
}
else if( strcmp( userInput, "QUIT" ) ==0)
keepRunning = false; // we're finished
else
printf("ERROR: Unknown command \"%s\"!\n\n", userInput );
}

void DoCleanUp( void )
{
if( gDatabase != NULL ) // We have allocated memory?
{
free( gDatabase );
gDatabase = NULL; // Not really necessary, but good style.
gNumDatabaseEntries = 0;
}
}
Comment 13 by Someone http://masters-of-the-void.com/book9.htm#comment13 http://masters-of-the-void.com/book9.htm#comment13 Whenever I try to enter any text (ie after "Artist Name:") I get this
[Session started at 2010-03-21 09:04:39 -0700.]
GNU gdb 6.3.50-20050815 (Apple version gdb-967) (Tue Jul 14 02:11:58 UTC 2009)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".sharedlibrary apply-load-rules all
Attaching to process 31286.
Comment 12 by Uli Kusterer http://masters-of-the-void.com/book9.htm#comment12 http://masters-of-the-void.com/book9.htm#comment12 Uli Kusterer writes:
Martin, thank you, corrected. Sometimes the server eats backslashes because it's trying to be helpful and keep people from injecting code...
Comment 11 by Ardeshir Sepahsalar http://masters-of-the-void.com/book9.htm#comment11 http://masters-of-the-void.com/book9.htm#comment11 Ardeshir Sepahsalar writes:
Here is a DoNewCommand and a helper function to check pointers, with some improvements, it lets you create names of artists and albums with spaces in them using getchar() :

int PtrNotOk( void *check ) {

bool status = false;

if (check == NULL) {
printf("Error: Couldn't create entry!\n");
status = true;
return status;
} else {
return status;
}
}

void doNewCommand() {

printf("Ok, lets create a new entry in the database.\n");

char yesOrNo; // this is to check if the entry is Sampler or not.
int c = 0; // array counter, for getchar()
// first create a new array element (or a new array if we don't have one

if (gDB == NULL) {

gDB = malloc( sizeof(struct dbCD) ); // size of 1 database

if ( PtrNotOk(gDB) ) {
return;
}

} else {

struct dbCD* newPtr = NULL;

newPtr = realloc( gDB, (gNumDbEntries+1) * sizeof( struct dbCD ) );

if( PtrNotOk(newPtr) ) {
return;
}

// newPtr is our new ptr, gDB is no longer valid
gDB = newPtr;
}

// make sure we remember we added one more entry

gNumDbEntries += 1;


printf("Enter Artist Name: ");

while((gDB[gNumDbEntries-1].artist[c++] = getchar()) != '\n')
;
fpurge(stdin);
gDB[gNumDbEntries-1].artist[--c] = 0; c=0; // remove the new line


printf("Enter Composer: ");
while((gDB[gNumDbEntries-1].composer[c++] = getchar()) != '\n')
;
fpurge(stdin);
gDB[gNumDbEntries-1].composer[--c] = 0; c=0;

printf("Enter Album Name: ");
while((gDB[gNumDbEntries-1].albumName[c++] = getchar()) != '\n')
;
fpurge(stdin);
gDB[gNumDbEntries-1].albumName[--c] = 0; c=0;

printf("No. of Tracks: ");
scanf("%d", &gDB[ gNumDbEntries-1].tracks ); // get the address of the Int for scanf
fpurge(stdin);

printf("Sampler? (y/n) ");
scanf("%c", &yesOrNo );
fpurge(stdin);

gDB[ gNumDbEntries-1].isSampler = (yesOrNo == 'y' || yesOrNo == 'Y');


}
Comment 10 by Martin Baker http://masters-of-the-void.com/book9.htm#comment10 http://masters-of-the-void.com/book9.htm#comment10 Missing backslash in DoNewCommand code?

printf( "ERROR: Couldn't create a new entry!n" );
Comment 9 by John http://masters-of-the-void.com/book9.htm#comment9 http://masters-of-the-void.com/book9.htm#comment9 Hi,

First of all, thanks for writing these tutorials. I've never found other tutorials intuitive to follow, but this definitely helps!

I'm just confused about two points in this chapter:

1. Inside the function DoNewCommand() with this line in particular:

scanf( "%d", &gDatabase[ gNumDatabaseEntries -1 ].trackCount );

is the reason there's a "&" character for trackCount because it's still an integer inside the CDDatabase structure and not a pointer like the character arrays? I just assumed that since I made a pointer to the entire CDDatabase structure that you wouldn't need that "&" character so I guess I'm wrong?

2. My other question is somewhat related to the above, but it deals with the code in the DoListCommand() function and the following lines of code:

printf( "Artist Name: %s\n", gDatabase[ x ].artist );
printf( "Composer: %s\n", gDatabase[ x ].composer );
printf( "Album Name: %s\n", gDatabase[ x ].albumName );
printf( "No. of Tracks: %d\n", gDatabase[ x ].trackCount );

Why isn't there a "*" character in front of the pointers for the artist, compose, and albumName character arrays? I thought it would be written as follows:

printf( "Artist Name: %s\n", *gDatabase[ x ].artist );
printf( "Composer: %s\n", *gDatabase[ x ].composer );
printf( "Album Name: %s\n", *gDatabase[ x ].albumName );
printf( "No. of Tracks: %d\n", gDatabase[ x ].trackCount );

with trackCount not having a "*" in front since it's still a variable within the CDDatabase structure unlike the character arrays inside which are pointers themselves.
Comment 8 by Richard Howell http://masters-of-the-void.com/book9.htm#comment8 http://masters-of-the-void.com/book9.htm#comment8 I just just down the compiler and restarted it.
The program now works fine, lol.

????
Comment 7 by Richard Howell http://masters-of-the-void.com/book9.htm#comment7 http://masters-of-the-void.com/book9.htm#comment7 I think I copied the code word for word, and at first it worked fine. But now I get this error, and to be fair, I don't understand it at all.

Is there something wrong with one of the library files?

Internal error occurred while creating dependency graph: _registerUndoObject:: NSUndoManager 0x2016497a0 is in invalid state, must begin a group before registering undo
Comment 6 by tómppu http://masters-of-the-void.com/book9.htm#comment6 http://masters-of-the-void.com/book9.htm#comment6 Thanks for the great tutorials Uli, some parts have been really hard to comprehend but that problem can be solved by trying harder. Learning new things is never easy, but you have managed to simplify this one and most of all made it flow so its interesting and fun!

This chapter however has one obvious flaw in the sample code;
Artist name, composer or album name can't contain any spaces or at least they're not printed out in the LISTing. One would think that it doesn't matter what goes inside "%39s" since it waits for a string and gets one but apparently it does... Any way to correct this?

Thanks!
Comment 5 by Uli Kusterer http://masters-of-the-void.com/book9.htm#comment5 http://masters-of-the-void.com/book9.htm#comment5 Uli Kusterer writes:
Ryan, gsoli, I hope the new animations will make re-reading this tutorial much more pleasant and understandable than the old graphics with their forests of arrows. Let me know what you think!
Comment 4 by Uli Kusterer http://masters-of-the-void.com/book9.htm#comment4 http://masters-of-the-void.com/book9.htm#comment4 Uli Kusterer writes:
stijnschoor, it is definitely possible to do that: There are variants of the functions you already know that work on files instead of the screen. Google for information on the "C standard library", particularly the functions fprintf(), fscanf(), fopen(), fwrite(), fread(), fseek(), ftell() and fclose().
Comment 3 by gsoli http://masters-of-the-void.com/book9.htm#comment3 http://masters-of-the-void.com/book9.htm#comment3 Very cool! This tutorial clarifies a lot of things for me. I especially enjoyed the explanation on the stack and heap in memory. These last few pages have been a little heavy, so I will have to come back again to try to force these concepts into my stubborn brain. It has been fun. Thanks!
Comment 2 by Ryan Stonebraker http://masters-of-the-void.com/book9.htm#comment2 http://masters-of-the-void.com/book9.htm#comment2 Ryan Stonebraker writes:
These Last two lesson have been very confusing... but after reading them twice I somewhat get it...
Thanks for the tutorials though!!!
Comment 1 by stijnschoor http://masters-of-the-void.com/book9.htm#comment1 http://masters-of-the-void.com/book9.htm#comment1 Dear Uli

Thanks for the tutorials, they have been a great help to me.
I am also creating a database(not CD's) and your article have helped me alot. Thanks.
Maybe you can help me with a question about C, in my database program I want the user to edit the data. Is there a way of printing the data from a file(In my case the FILE pointer file) and let the user edit it? I was thinking about some sort of scanf function where there's already some text.

Thank you
Stijn