Background:
I'm trying to create a program that takes a user name(assuming that input is clean), and prints out the initials of the name.
Objective:
Trying my hand out at C programming with CS50
Getting myself familiar with malloc & realloc
Code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
string prompt(void);
char *getInitials(string input);
char *appendArray(char *output,char c,int count);
//Tracks # of initials
int counter = 0;
int main(void){
string input = prompt();
char *output = getInitials(input);
for(int i = 0; i < counter ; i++){
printf("%c",toupper(output[i]));
}
}
string prompt(void){
string input;
do{
printf("Please enter your name: ");
input = get_string();
}while(input == NULL);
return input;
}
char *getInitials(string input){
bool initials = true;
char *output;
output = malloc(sizeof(char) * counter);
for(int i = 0, n = strlen(input); i < n ; i++){
//32 -> ASCII code for spacebar
//9 -> ASCII code for tab
if(input[i] == 32 || input[i] == 9 ){
//Next char after spaces/tab will be initial
initials = true;
}else{//Not space/tab
if(initials == true){
counter++;
output = appendArray(output,input[i],counter);
initials = false;
}
}
// eprintf("Input[i] is : %c\n",input[i]);
// eprintf("Counter is : %i\n",counter);
// eprintf("i is : %i\n",i);
// eprintf("n is : %i\n",n);
}
return output;
}
char *appendArray(char *output,char c,int count){
// allocate an array of some initial (fairly small) size;
// read into this array, keeping track of how many elements you've read;
// once the array is full, reallocate it, doubling the size and preserving (i.e. copying) the contents;
// repeat until done.
//pointer to memory
char *data = malloc(0);
//Increase array size by 1
data = realloc(output,sizeof(char) * count);
//append the latest initial
strcat(data,&c);
printf("Value of c is :%c\n",c);
printf("Value of &c is :%s\n",&c);
for(int i = 0; i< count ; i++){
printf("Output: %c\n",data[i]);
}
return data;
}
Problem:
The output is not what i expected as there is a mysterious P appearing in the output.
E.g When i enter the name Barack Obama, instead of getting the result:BO, i get the result BP and the same happens for whatever name i choose to enter, with the last initial always being P.
Output:
Please enter your name: Barack Obama
Value of c is :B
Value of &c is :BP
Output: B
Value of c is :O
Value of &c is :OP
Output: B
Output: P
BP
What i've done:
I've traced the problem to the appendArray function, and more specifically to the value of &c (Address of c) though i have no idea what's causing the P to appear,what it means, why it appears and how i can get rid of it.
The value of P shows up no matter when i input.
Insights as to why it's happening and what i can do to solve it will be much appreciated.
Thanks!
Several issues, in decreasing order of importance...
First issue - c in appendArray is not a string - it is not a sequence of character values terminated by a 0. c is a single char object, storing a single char value.
When you try to print c as a string, as in
printf("Value of &c is :%s\n",&c);
printf writes out the sequence of character values starting at the address of c until it sees a 0-valued byte. For whatever reason, the byte immediately following c contains the value 80, which is the ASCII (or UTF-8) code for the character 'P'. The next byte contains a 0 (or there's a sequence of bytes containing non-printable characters, followed by a 0-valued byte).
Similarly, using &c as the argument to strcat is inappropriate, since c is not a string. Instead, you should do something like
data[count-1] = c;
Secondly, if you want to treat the data array as a string, you must make sure to size it at least 1 more than the number of initials and write a 0 to the final element:
data[count-1] = 0; // after all initials have been stored to data
Third,
char *data = malloc(0);
serves no purpose, the behavior is implementation-defined, and you immediately overwrite the result of malloc(0) with a call to realloc:
data = realloc(output,sizeof(char) * count);
So, get rid of the malloc(0) call altogether; either just initialize data to NULL, or initialize it with the realloc call:
char *data = realloc( output, sizeof(char) * count );
Fourth, avoid using "magic numbers" - numeric constants with meaning beyond their immediate, literal value. When you want to compare against character values, use character constants. IOW, change
if(input[i] == 32 || input[i] == 9 ){
to
if ( input[i] == ' ' || input[i] == '\t' )
That way you don't have to worry about whether the character encoding is ASCII, UTF-8, EBCDIC, or some other system. ' ' means space everywhere, '\t' means tab everywhere.
Finally...
I know part of your motivation for this exercise is to get familiar with malloc and realloc, but I want to caution you about some things:
realloc is potentially an expensive operation, it may move data to a new location, and it may fail. You really don't want to realloc a buffer a byte at a time. Instead, it's better to realloc in chunks. A typical strategy is to multiply the current buffer size by some factor > 1 (typically doubling):
char *tmp = realloc( data, current_size * 2 );
if ( tmp )
{
current_size *= 2;
data = tmp;
}
You should always check the result of a malloc, calloc, or realloc call to make sure it succeeded before attempting to access that memory.
Minor stylistic notes:
Avoid global variables where you can. There's no reason counter should be global, especially since you pass it as an argument to appendArray. Declare it local to main and pass it as an argument (by reference) to getInput:
int main( void )
{
int counter = 0;
...
char *output = getInitials( input, &counter );
for(int i = 0; i < counter ; i++)
{
printf("%c",toupper(output[i]));
}
...
}
/**
* The "string" typedef is an abomination that *will* lead you astray,
* and I want to have words with whoever created the CS50 header.
*
* They're trying to abstract away the concept of a "string" in C, but
* they've done it in such a way that the abstraction is "leaky" -
* in order to use and access the input object correctly, you *need to know*
* the representation behind the typedef, which in this case is `char *`.
*
* Secondly, not every `char *` object points to the beginning of a
* *string*.
*
* Hiding pointer types behind typedefs is almost always bad juju.
*/
char *getInitials( const char *input, int *counter )
{
...
(*counter)++; // parens are necessary here
output = appendArray(output,input[i],*counter); // need leading * here
...
}
Related
During an interview, I was asked to implement a solution for following problem in C programming language:
Given an input string with a fixed pattern of contents separated by a delimiter, I need to extract each specific content.
Pattern of input string: "starting_message|integer_value_1|integer_value_2|character_code|ending_message"
Expected output:
Starting message: starting_message
Value 1 : integer_value_1
Value 2 : integer_value_2
Char code : character_code
Ending message : ending_message
Example input: "HelloWorld|35|45|C|ByeWorld"
Example output:
Starting message: HelloWorld
Value 1 : 35
Value 2 : 45
Char code : C
Ending message : ByeWorld
I implemented the following code:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
/*
Loop through the input string until termination.
Figure out the position of delimiters first. It will help in parsing later.
For string, count the number of chars and use memcpy for that number of elements.
To parse char to int, try atoi.
*/
void parse(const char* input, char* starting_message, int* value1, int* value2, char* char_code, char* ending_message)
{
int ctr = 0, pos_ctr=0;
int delim_pos[4]; /* To store the location of delimiters aka '|' */
char* value1_str = (char*) malloc(10);
char* value2_str = (char*) malloc(10);
while(input[ctr]!= '\0')
{
if(input[ctr] == '|')
{
if(pos_ctr < 4)
{
delim_pos[pos_ctr] = ctr;
pos_ctr++;
}
}
ctr++;
}
memcpy(starting_message,input,(delim_pos[0])); /* starting_message is contained in input string in between input[0] & input[delim_pos[0]]*/
starting_message[delim_pos[0]+1] = '\0';
memcpy(value1_str, input,(delim_pos[1]-delim_pos[0])); /* value1 is contained in input string in between input[delim_pos[0]] & input[delim_pos[1]]*/
value1_str[(delim_pos[1]-delim_pos[0] + 1)] = '\0';
*value1 = atoi(value1_str);
memcpy(value2_str, input,(delim_pos[2]-delim_pos[1])); /* value2 is contained in input string in between input[delim_pos[1]] & input[delim_pos[2]]*/
value1_str[(delim_pos[2]-delim_pos[1] + 1)] = '\0';
*value2 = atoi(value2_str);
*char_code = input[(delim_pos[2]+1)]; /* char_code is element next to input[delim_pos[2]]*/
memcpy(ending_message, input, (delim_pos[3]-ctr-1)); /* ending_message is contained in input string in between input[delim_pos[3]] & null termination char*/
ending_message[delim_pos[3]-ctr] = '\0';
}
int main() {
const char* input = "HelloWorld|35|45|C|ByeWorld";
char* starting_message = (char*) malloc(30);
char* ending_message = (char*) malloc(30);
int value1, value2;
char char_code;
parse(input, starting_message, &value1, &value2, &char_code, ending_message);
printf(" Input string: %s\n",input);
printf("Starting message : %s\n", starting_message);
printf("Value 1 : %d\n", value1);
printf("Value 2 : %d\n", value2);
printf("Character code : %c\n", char_code);
printf("Ending message: %s\n", ending_message);
return 0;
}
I was able to compile but on the output screen, I encountered a segmentation fault with no output.
Where did I go wrong and how to fix it?
Your segmentation fault occurs here:
memcpy(ending_message, input, (delim_pos[3]-ctr-1));
Typical causes for segmentation faults are null or garbage pointers, but here, it's something else: You have miscalculated the number of bytes to copy, because you got the subtraction the wrong way round. It should be
ctr + 1 - delim_pos[3]
because, if everything went well, str is past delim_pos[3]. (But you don't check that pos_ctr == 4, so you don't know whether everything went well.)
The third parameter to memcpy is of the unsigned integer type size_t. By mixing up the subtraction, you got a small negative int value, which will be a huge positive value when converted to an unsigned value, which certainly leads to out-of-bounds access.
For starters the function does not report whether parsing of the input string was successful.
The function has too many parameters. Instead of this list of parameters
char* starting_message, int* value1, int* value2, char* char_code, char* ending_message
you could pass an object of a structure type. For example the function declaration could be
int parse( const char *input, struct Fields *fields );
These memory allocations
char* value1_str = (char*) malloc(10);
char* value2_str = (char*) malloc(10)
are redundant and moreover due to these allocations the function produces memory leaks.
Instead of the function atoi you should use the function strtol.
Instead of this while loop
while(input[ctr]!= '\0')
you could use a loop within which there is called the function strchr.
In this assignment
starting_message[delim_pos[0]+1] = '\0';
the character at the position delim_pos[0] can have indeterminate value. Instead of the statement above you have to write
starting_message[delim_pos[0]] = '\0';
In these statements
memcpy(value1_str, input,(delim_pos[1]-delim_pos[0]));
memcpy(value2_str, input,(delim_pos[2]-delim_pos[1]));
memcpy(ending_message, input, (delim_pos[3]-ctr-1));
you are copying characters beginning from the start of the string input that does not make a sense. Moreover this expression delim_pos[3]-ctr-1 will yield a negative value.
This statement
ending_message[delim_pos[3]-ctr] = '\0';
invokes undefined behavior because the value of the expression delim_pos[3]-ctr is negative. ctr stores the length of the input string.
Thus the function has undefined behavior and does not make a great sense.:)
So I am working away on the 'less comfortable' version of the initials problem in CS50, and after beginning with very verbose code I've managed to whittle it down to this:
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int c = 0;
int main(void)
{
string name = get_string();
int n = strlen(name);
char initials[10];
// first letter is always going to be the first initial
initials[0] = name[0];
// count through letters looking for spaces + add the first letter after a
// space to the initials array
for (int j = 0; j < n; j++)
{
if (name[j] == 32)
{
c += 1;
initials[c] += name[j+1];
}
}
// print out initials
for (int k = 0; k <= c; k++)
{
printf("%c", toupper(initials[k]));
}
printf("\n");
}
As it stands like that it passes, but I feel like I am copping out a little cos I just pick [10] out of the air for the initial array size which I know isn't good practice. To make it a little 'better' I've tried to run a 'for' loop to iterate through the name string and add up the number of spaces. I then want to make the array [spaces + 1] as if there are 2 spaces then there will be 3 initials. The code I am trying for that is:
string name = get_string();
int n = strlen(name);
for (int i = 0; i < n; i++)
{
if (name[i] == 32)
{
spaces +=1;
}
}
The thought is that I then make 'char initials[spaces + 1]' on the next line, but even before I can do that, compiling my code with just this 'for' loop returns a fail when I upload it for checking (although it compiles no problem). Even if I don't use any of the 'for' loops output the mere fact it is there gives me this error.
Where am I going wrong?
Any help on this would be much appreciated.
Thanks!
First of all, keep in mind that execution speed is most often more valuable than memory use. If you first go look for spaces and after that allocate memory, you have to iterate through the array twice. This is an optimization of memory use at the cost of execution speed. So it might make more sense to just allocate a "large enough" array of lets say 100 characters and keep the code that you have.
I then want to make the array [spaces + 1] as if there are 2 spaces then there will be 3 initials
Keep in mind that C strings are null terminated, so you need to allocate room for the null terminator too, spaces + 1 + 1.
compiling my code with just this 'for' loop returns a fail when I upload it for checking (although it compiles no problem). Even if I don't use any of the 'for' loops output the mere fact it is there gives me this error.
What error? Does it compile or does it not compile, your text is contradicting.
Make sure you initialize spaces to zero.
As a side note, never use "magic numbers" in C code. if (name[i] == 32), 32 is gibberish to anyone who can't cite the ASCII table by memory. In addition, it is non-portable to systems with other symbol tables that might not have the same index numbers. Instead write:
if (name[i] == ' ')
In my opinion, a good approach to cater for such situations is the one the library function snprintf uses: It requires you to pass in the string to fill and the size of that string. In ensures that the string isn't overwritten and that the string is zero-terminated.
The function returns the length of the characters written to the string if the had the string been large enough. You can now do one of two things: Guess a reasonable buffer size and accept that the string will be cut short occasionally. Or call the function with a zero length, use the return value to allocate a char buffer and then fill it with a second call.
Applying this approach to your initials problem:
int initials(char *ini, int max, const char *str)
{
int prev = ' '; // pretend there's a space before the string
int n = 0; // actual number of initials
while (*str) {
if (prev == ' ' && *str != ' ') {
if (n + 1 < max) ini[n] = *str;
n++;
}
prev = *str++;
}
if (n < max) {
ini[n] = '\0';
} else if (max > 0) {
ini[max] = '\0';
}
return n;
}
You can then either use the fixed-size bufer approach:
char *name = "Theodore Quick Brown Fox";
char ini[4];
initials(ini, sizeof(ini), name);
puts(ini); // prints "TQB", "F" is truncated
Or the two-step dynamic-size approach:
char *name = "Theodore Quick Brown Fox";
int n;
n = initials(NULL, 0, name);
char ini[n + 1];
initials(ini, sizeof(ini), name);
puts(ini); // prints "TQBF"
(Note that this implementation of initals will ignore multiple spaces and spaces at the end or at the beginning of the string. Your look-one-ahead function will insert spaces in these cases.)
You know your initials array can't be any bigger than the name itself; at most, it can't be more than half as big (every other character is a space). So use that as your size. The easiest way to do that is to use a variable-length array:
size_t n = strlen( name ); // strlen returns a size_t type, not int
char initials[n/2+1]; // n/2+1 is not a *constant expression*, so this is
// a variable-length array.
memset( initials, 0, n + 1 ); // since initials is a VLA, we can't use an initializer
// in the declaration.
The only problem is that VLA support may be iffy - VLAs were introduced in C99, but made optional in C2011.
Alternately, you can use a dynamically-allocated buffer:
#include <stdlib.h>
...
size_t n = strlen( name );
char *initials = calloc( n/2+1, sizeof *initials ); // calloc initializes memory to 0
/**
* code to find and display initials
*/
free( initials ); // release memory before you exit your program.
Although, if all you have to do is display the initials, there's really no reason to store them - just print them as you find them.
Like others have suggested, use the character constant ' ' instead of the ASCII code 32 for comparing against a space:
if ( name[j] == ' ' )
or use the isspace library function (which will return true for spaces, tabs, newlines, etc.):
#include <ctype.h>
...
if ( isspace( name[j] ) )
I am new to C programming and pointers.
I made a simple program where I can read in the string and the program tells you how many characters are there and how many alphabets had appeared how many times.
Somehow, my output is not right. I think it might be my pointer and dereferencing problem.
here is my main:
extern int* count (char* str);
int main(int argc, char* argv[])
{
int numOfChars =0;
int numOfUniqueChars = 0;
char str[80];
int *counts;
strcpy(str, argv[1]);
printf("counting number of characters for \"%s\"..\n", str);
printf("\n");
counts = count(str);
int j;
for (j =0; j<sizeof(counts); j++)
{
if(counts[j])
printf("character %c", *str);
printf("appeared %d times\n", counts[j]);
numOfChars++;
numOfUniqueChars++;
}
printf("\"%s\" has a total of %d character(s)\n", str, numOfChars);
printf(wow %d different ascii character(s) much unique so skill\n", numOfUniqueChars);
}
and this is my count function:
int* count(char* str)
{
int* asctb = malloc(256);
int numOfChars =0;
int i;
int c;
for(i = 0; i<strlen(str); i++)
c = str[i];
asctb[c]++;
numOfChars += strlen(str);
return asctb;
}
and when I compile and run it, my result comes up like this:
./countingCharacter doge
counting number of characters for "doge"...
appeared 0 times
appeared 0 times
appeared 0 times
appeared 0 times
"doge" has a total of 4 character(s)
wow 4 different ascii character(s) much unique so skill
But, I want my result to be like this:
Character d appeared 1 times
Character e appeared 1 times
Character g appeared 1 times
Character o appeared 1 times
"doge" has a total of 4 character(s)
wow 4 different ascii character(s) much unique so skill
Any help will be much appreciated.
Thanks in advance!
EDIT:
i added curly braces for my for loop in the main function.
now i get this result:
./countingCharacter doge
character # appeared 7912 times
character d appeared 1 times
character e appeared 1 times
character g appeared 1 times
character o appeared 1 times
why do I get that "#" in the beginning??
As #kaylum said, one particularly large issue is your use of braces. If you don't use braces with a control flow statement (if, for, while, etc.), only the next line is counted as a part of that statement. As such, this segment:
if (counts[j])
printf("character %c", *str);
printf("appeared %d times\n", counts[j]);
/* ... */
...will only execute the first printf if counts[j] != 0, but will unconditionally execute the following statements.
Your use of malloc is also incorrect. malloc(256) will only allocate 256 bytes; one int is generally 4 bytes, but this differs based on the compiler and the machine. As such, when malloc'ing an array of any type, it's good practice to use the following technique:
type *array = malloc(element_count * sizeof(type));
In your case, this would be:
int *asctb = malloc(256 * sizeof(int));
This ensures you have room to count all the possible values of char. In addition, you'll have to change the way you iterate through counts as sizeof (counts) does not accurately represent the size of the array (it will most likely be 4 or 8 depending on your system).
The variable numOfChars will not behave the way you expect it to. It looks to me like you're trying to share it between the two functions, but because of the way it's declared this will not happen. In order to give global access to the variable, it needs to be declared at global scope, outside of any function.
Also, the line:
printf("character %c ", *str);
...neither keeps track of what characters you've printed nor which you're supposed to, instead just repeatedly printing the first character. *str should be (char)j, since you're printing ASCII values.
That ought to do it, I think.
If you are new to C, there are a number of issues in your code you need to pay attention to. First, if a function returns a value, validate that value. Otherwise, from that point in your code on, you can have no confidence that it is actually operating on the value or memory location you think it is. For example, each of the following should be validated (or changed to stay within allowable array bounds):
strcpy(str, argv[1]);
int* asctb = malloc(256);
counts = count(str);
What if argv[1] had 100 chars? What if malloc returned NULL? How do you know count succeeded? Always include the necessary validations needed by your code.
While not an error, the standard coding style for C avoids caMelCase variables in favor of all lower-case. See e.g. NASA - C Style Guide, 1994 So
int numOfChars =0;
int numOfUniqueChars = 0;
could simply be nchars and nunique.
Next, all your if and for loop syntax fails to encapsulate the required statements in braces, e.g. {...} to create a proper block for your if or for. For example, the following:
for(i = 0; i<strlen(str); i++)
c = str[i];
asctb[c]++;
only loops over c = str[i]; and asctb[c]++; is only executed AFTER the loop exits.
You must initialize your variable, (especially your array elements) before you attempt to reference them otherwise undefined behavior results. (it could seem to work, give weird output like a strange "#" character, or segfault, that's why it is undefined). You have a big problem here:
int* asctb = malloc(256);
None of the values in asctb are initialized. So when you return the array to main() and loop over all values in the array, every element that was not explicitly assigned a value causes undefined behavior. You can either set all values to 0 with memset, or recognize when you need all values initialized and use calloc instead:
int *asctb = calloc (1, 256);
Avoid the use of "magic-numbers" in your code. 256 above is a great example. Don't litter you code with these magic-numbers, instead defined a constant for them at the beginning of your code with either #define or for numerical constants, use an enum instead.
Lastly, in any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed using free when it is no longer needed. You should validate your memory use by running your code though a Memory Error Checking Program, such as valgrind on Linux. It's simple to do and will save you from yourself more times than you can imagine.
Putting all these pieces together and fixing additional logic errors in your code, you look like you were attempting something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* constants for max characters in str and values in asctb */
enum { MAXC = 80, MAXTB = 128 };
int *count (char *str);
int main (int argc, char **argv) {
if (argc < 2) { /* validate str given as argument 1 */
fprintf (stderr, "error: insufficient input, usage: %s str.\n",
argv[0]);
return 1;
}
/* initialize all variables avoid CamelCase names in C */
char str[MAXC] = "";
int j = 0, nchars = 0, nunique = 0;
int *counts = NULL;
strncpy (str, argv[1], MAXC - 1); /* limit copy len */
str[MAXC - 1] = 0; /* nul-terminate str */
printf ("\ncounting number of characters for \"%s\"..\n\n", str);
if (!(counts = count (str))) { /* validate return */
fprintf (stderr, "error: count() returned NULL.\n");
return 1;
}
for (j = 0; j < MAXTB; j++)
if (counts[j]) {
printf ("character '%c' appeared: %d times\n",
(char)j, counts[j]);
nchars += counts[j];
nunique++;
}
free (counts); /* free allocated memory */
printf ("\n\"%s\" has a total of %d character(s)\n", str, nchars);
printf (" wow %d different ascii character(s) much unique so skill\n\n",
nunique);
return 0; /* main is a function of type 'int' and returns a value */
}
int *count (char *str)
{
if (!str) return NULL; /* validate str */
int *asctb = calloc (1, sizeof *asctb * MAXTB);
size_t i; /* you are comparing with size_t in loop */
if (!asctb) { /* validate memory allocation - always */
fprintf (stderr, "count() error: virtual memory exhausted.\n");
return NULL;
}
for(i = 0; i < strlen(str); i++)
asctb[(int)str[i]]++; /* array indexes are type 'int' */
return asctb;
}
(note: the first 30 characters in counts are in the non-printable range, see ASCIItable.com. The indexes were left as you had them, but note, in practice you may want to shift them unless you are interested in counting the non-printable \t, \n, etc. chars).
Example Use/Output
$ ./bin/ccount "address 12234"
counting number of characters for "address 12234"..
character ' ' appeared: 1 times
character '1' appeared: 1 times
character '2' appeared: 2 times
character '3' appeared: 1 times
character '4' appeared: 1 times
character 'a' appeared: 1 times
character 'd' appeared: 2 times
character 'e' appeared: 1 times
character 'r' appeared: 1 times
character 's' appeared: 2 times
"address 12234" has a total of 13 character(s)
wow 10 different ascii character(s) much unique so skill
Look over the logic and syntax corrections and let me know if you have any further questions.
I thought I had this solved, but apparently, I was incorrect. The question is... what did I miss?
Assignment description:
You are to create a C program which fills an integer array with integers and then you are to cast it as a string and print it out. The output of the string should be your first and last name with proper capitalization, spacing and punctuation. Your program should have structure similar to:
main()
{
int A[100];
char *S;
A[0]=XXXX;
A[1]=YYYY;
...
A[n]=0; -- because C strings are terminated with NULL
...
printf("My name is %s\n",S);
}
Response to my submission:
You still copied memory cells to other, which is not expected. You use different space for the integer array as the string which does not follow the requirements. Please follow the instructions carefully next time.
My submission
Note that the first time I submitted, I simply used malloc on S, and copied casted values from A to S. The response was that I could not use malloc or allocate new space. This requirement was not in the problem description above.
Below was my second and final submission, which is the submission being referred to in the submission response above.
#include <stdio.h>
/* Main Program*/
int main (int arga, char **argb){
int A[100];
char *S;
A[0] = 68;
A[1] = 117;
/** etc. etc. etc. **/
A[13] = 115;
A[14] = 0;
// Point a char pointer to the first integer
S = (char *) A;
// For generality, in C, [charSize == 1 <= intSize]
// This is the ratio of intSize over charSize
int ratio = sizeof(int);
// Copy the i'th (char sized) set of bytes into
// consecutive locations in memory.
int i = 0;
// Using the char pointer as our reference, each set of
// bits is then i*ratio positions away from the i'th
// consecutive position in which it belongs for a string.
while (S[i*ratio] != 0){
S[i] = S[i*ratio];
i++;
}
// a sentinel for the 'S string'
S[i] = 0;
printf("My name is %s\n", S);
return 0;
}// end main
It looks like you've got the core idea down: the space for one integer will hold many chars. I believe you just need to pack the integer array "by hand" instead of in the for loop. Assuming a 4-byte integer on a little-endian machine, give this a shot.
#include <stdio.h>
int main()
{
int x[50];
x[0] = 'D' | 'u' << 8 | 's' << 16 | 't' << 24;
x[1] = 0;
char *s = (char*)x;
printf("Name: %s\n", s);
return 0;
}
It sounds like your professor wanted you to put 4 bytes into each int instead of having an array of n "1 byte" ints that you later condensed into 4 / sizeof(int) bytes using the while loop. Per Hurkyl's comment, the solution to this assignment would be platform dependent, meaning that it will differ from machine to machine. I'm assuming your instructor had the class ssh into and use a specific machine?
In any case, assuming you're on a little endian machine, say you wanted to type out the string: "Hi Dad!". Then a snippet of the solution would look something like this:
// Precursor stuff
A[0] = 0x44206948; // Hi D
A[1] = 0x216461; // ad!
A[2] = 0; // Null terminated
char *S = (char *)A;
printf("My string: %s\n", S);
// Other stuff
In C, I want to check a given array of chars for an arbitrary letter, and change it according to what it is. For example, the characters "a" or "A" would be changed to "4"(the character representing 4). This is a coding excercise for me :)
The code is as follows:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <zlib.h>
#define NUM_BUFFERS 8
#define BUFFER_LENGTH 1024
char buffArrays[NUM_BUFFERS][BUFFER_LENGTH];
int main(int argc, const char* arg[v])
{
const char a[] = "a";
gzFile file;
file = gzopen("a.txt", "rb"); //contains 8 lines of 1024 'a's
int counter = 0;
while(counter < NUM_BUFFERS)
{
gzread(file, buffArrays[counter], BUFFER_LENGTH - 1);
counter++;
}
counter = 0;
while(counter < NUM_BUFFERS)
{
int i = 0;
for( i; i < BUFFER_LENGTH; i++ )
{
int *changed = &buffArrays[counter][i];
if( memcmp(&a, changed, 1) == 0 )
printf("SUCCESS\n");
}
counter++;
}
gzclose(file);
return 0;
}
This code never reaches the "SUCCESS" part. This says to me that either
(1) the value of changed is not pointing to the correct thing
(2) the pointer &a is incorrect
(3) I am completely wrong and it is something else
Any help would be appreciated.
Two things.
The following assigns the value 0x61 or 'a' to the character string.
const char a[] = 'a';
You probably rather meant to write
const char a = 'a'; /* assign a character to a character */
or
const char a[] = "a"; /* assign a string to a string */
The next thing is with the following statement. Hereby you assign a pointer to an int with the memory address of a char. Which invokes undefined behavior as you are reading over the bounds of your valid memory in the next statement.
int *changed = &bufferArrays[counter][i];
Hereby you compare the first four bytes starting from both addresses. Both variables are only one byte wide.
if( memcmp(&a, changed, 4) == 0 )
If you only want to know whether there is an 'a' in some of your buffer, why don't you just.
int i, j;
for (i = 0; i < NUM_BUFFERS; i++)
for (j = 0; j < BUFFER_LENGTH; j++)
if (bufferArrays[i][j] == 'a') printf("got it!\n");
This:
bufferArrays[counter] = "a"; //all the buffers contain one "a"
is wrong, since bufferArrays[counter] is not a character pointer but a character array. You need:
strcpy(bufferArrays[counter], "a");
Also, you don't show readTOmodify, so that part is a bit hard to understand.
Further, strings are best compared with strcpy(), which compares character-by-character and stops at the terminating '\0'. You use memcmp(), and I don't understand the reason for the 4 which is the number of bytes you're comparing.
1) bufferArrays[counter] = "a"; //all the buffers contain one "a"
This is not ok, you have to use strcpy to copy strings:
strcpy(bufferArrays[counter],"a"); //all the buffers contain one "a"
2)
#define BUFFER_LENGTH 1
Here's a problem. Buffer length should be at least 2 if you want to store just one char (for the extra null-termination).
3) In both of your loops, you never change counter, which leads to infinite loop.
Where's your code? I don't see any function surrounding it.
EDIT:
To assign you can also use:
while(counter < NUM_BUFFERS)
{
bufferArrays[counter][0] = 'a'; //all the buffers contain one "a"
counter++;
}
In any case, you have to have Buffer length as 2 if you want use it as a C-string.
The statement
bufferArrays[counter] = "a";
is not legal. It assigns a pointer to a single char and should give a compiler error (or at least a warning). Instead try
bufferArrays[counter] = 'a';
Also, in the while loops (both of them) you do not increase counter and so loop over the same index over and over forever.
Edit: Further problems
The condition where you do the comparison is flawed as well:
memcmp(&a, changed, 4)
The above doesn't compare pointers, it compares the contents of what the pointers point to, and you compare four bytes while the contents is only a single byte. Besides, you can't compare the pointers, as they will be different; The contents of the variable a is stored at a different location than that of the contents of bufferArrays[counter][i].