I am trying to compute the most frequent digit in a string of characters, and I need to use pointers but I am not sure how to go about this with pointers.
int most(char* string){
int counter = 0;
int* array =(int*) malloc(sizeof(int)*10);
char* original = string;
while(*original){
counter++;
string++;
//Not sure what to put in this loop
}
}
for example, I want to call the code
char nums[] = "132433423";
printf("%d \n",most(nums));
// 3
The specification for your function is incomplete:
can the string contain non-digit characters?
what should be returned if there are no digits at all?
which value should be returned if there are multiple digits with the same maximum number of occurrences?
should the function return the digit or its numeric value? Your main() function uses the latter, but it is not clear from the text of the question.
The most function receives a pointer to the string. You can write a loop where you handle one character at a time and increment the pointer for the next iteration until you reach the end of the string. You must also decide what to return if the string contains no digits.
Here is a simple example:
int most(const char *s) {
int count[10] = { 0 };
int res, i, max;
while (*s) {
if (*s >= '0' && *s <= '9')
count[*s - '0']++;
s++;
}
res = -1; /* return -1 if no digits */
max = 0;
for (i = 0; i < 10; i++) {
if (count[i] > max)
res = i;
}
return res;
}
If you are restricted from using any array at all, allocating a block of memory seems indeed a good solution:
int most(const char *s) {
int *count = calloc(sizeof(*count), 10);
int res, i, max;
while (*s) {
if (*s >= '0' && *s <= '9')
*(count + *s - '0') += 1;
s++;
}
res = -1; /* return -1 if no digits */
max = 0;
for (i = 0; i < 10; i++) {
if (*(count + i) > max)
res = i;
}
free(count);
return res;
}
The notation *(count + *s - '0') += 1 works this way: count is a pointer to an array of int allocated and initialized to 0 by calloc. *s - '0' is the digit value n of the character pointed to by s, that has been tested to be a digit. count + *s - '0' is a pointer to the n-th entry in the array. *(count + *s - '0') += 1 increments this value by one.
There are ways to do this without memory allocation, with 10 variables and explicit tests for the different digits, but I doubt this be the expected solution.
If you can explain your choices to your teacher, there are 2 ways to use arrays without the [ and ] characters. These are obsolescent features of the C Standard, which most programmers are not familiar with, and which you can ignore unless you are curious:
int most(const char *s) { /* version with C99 digraphs */
int count<:10:> = { 0 };
int res, i, max;
while (*s) {
if (*s >= '0' && *s <= '9')
count<:*s - '0':>++;
s++;
}
res = -1; /* return -1 if no digits */
max = 0;
for (i = 0; i < 10; i++) {
if (count<:i:> > max)
res = i;
}
return res;
}
Or
int most(const char *s) { /* version with old-style trigraphs */
int count??(10??) = { 0 };
int res, i, max;
while (*s) {
if (*s >= '0' && *s <= '9')
count??(*s - '0'??)++;
s++;
}
res = -1; /* return -1 if no digits */
max = 0;
for (i = 0; i < 10; i++) {
if (count??(i??) > max)
res = i;
}
return res;
}
You could first sort the string so that the smaller digit characters appear first in num. You could use qsort() (from stdlib.h) like
int cmpfn(const void *a, const void *b)
{
int x = *(char *)a;
int y = *(char *)b;
return x-y;
}
int main()
{
char nums[] = "132433423";//"111222223333";//
qsort(nums, sizeof(nums)/sizeof(nums[0]) -1, sizeof(nums[0]), cmpfn);
printf("\nAfter sorting: %s", nums);
. . . . . . . . . .
. . . . . . . . . .
}
Declare variables to store the mode (ie, the value that appears most frequently in the data) and the frequency of the mode value.
int mode=-1, modecount=-1, n;
Now find the frequency of each digit character. Since this is a sorted character array, the same value will appear consecutively.
for(char *ptr=nums, *lptr=NULL; *ptr; ptr+=n)
{
lptr = strrchr(ptr, *ptr);
n = lptr - ptr + 1;
if(n>modecount)
{
printf("\n%c", *ptr);
modecount = n;
mode = *ptr;
}
}
printf("\nMode is: %c", mode);
strrchr() (from string.h) will find the last occurrence of a character in a string.
#include<string.h>
int most (char* nums) {
int i, max_index = 0;
int digit_dictionary[10]={0,0,0,0,0,0,0,0,0,0};
for (i=0; i< strlen(nums); i++) {
digit_dictionary[nums[i]-'0'] ++;
}
for (i=1; i<10; i++) {
if (digit_dictionary[i]> digit_dictionary[max_index])
max_index = i;
}
return max_index;
}
I will try to be as elaborate as I can:
You create a dictionary in which each index corresponds to a digit that can occur (0-9). Then, iterate through the string(which is basically an array of characters, and store each digit to its corresponding index in dictionary.
Note: [nums[i]-'0'] is calculated into the index of the dictionary since each char has an integer value (look up ASCII table). The counter at that index is incremented to keep count of number of occurrences of that digit.
After that, go through the dictionary to determine at which position is the digit with most occurrences, and return that digit.
I'm not sure what you mean by "using pointers", but here's a version which doesn't use pointers except for walking the input string:
char most_frequent_character(char *s)
{
int freq[10];
int max_freq;
int max_idx;
int idx;
while(*s)
freq[*s++ - '0']++; /* compute character freqs */
max_idx = 0;
max_freq = freq[0];
for(idx = 1 ; idx < 10 ; ++idx)
if(freq[idx] > max_freq)
{
max_freq = freq[idx];
max_idx = i;
}
return '0' + max_idx;
}
Have fun.
EDIT
To convert the above to "use pointers":
A. Change freq to a pointer-to-int and initialize it using malloc; in addition, initialize the memory pointed to by freq using memset:
int *freq = malloc(sizeof(int) * 10);
memset(freq, 0, sizeof(int)*10);
B. In the "compute character freqs" loop, use pointer references instead of indexing:
while(*s)
{
*(freq + (*s - '0')) = *(freq + (*s - '0')) + 1;
s++;
}
C. Use a pointer ref to set the initial value of max_freq:
max_freq = *freq;
D. In the for loop, use pointer math instead of indexing:
for(idx = 1 ; idx < 10 ; ++idx)
if( *(freq + idx) > max_freq)
{
max_freq = *(freq + idx);
max_idx = i;
}
E. Free the memory allocated earlier before the return statement:
free(freq);
return '0' + max_idx;
Now, sit down and understand why things are done in the way that they are here. For example, why didn't I do the following when computing the character frequencies?
while(*s++)
*(freq + (*s - '0')) = *(freq + (*s - '0')) + 1;
or
while(*s)
*(freq + (*s++ - '0')) = *(freq + (*s++ - '0')) + 1;
Each of the above would save several lines of code - why shouldn't they be used? (The obvious answer is "because they won't work as intended" - but WHY?)
Best of luck.
Related
I am trying to create a function that scans the user input and shows the counted integer numbers and add them. For example the user input would be 'room 2301 next to 12 street' the function would count the number 2301 as one, and 12 as two and add them, 2313 which is the sum and returns it. so the return would be count=2, sum= 2313. However, I am having an issue with the logic behind the function. Currently my function takes the numbers separately, eg. 2302 = 2,3,0,2.
Here is my code:
void num_count(char array[]) {
int i = 0;
int count = 0;
int sum = 0;
int tmp[20];
tmp_size = 20;
while (array[i] != '\0') {
if (array[i] >= '0' && array[i] <= '9') {
tmp[i] = (array[i] - '0');
//not sure what to do here
count++;
sum += (array[i] - '0');
}
i++;
}
}
currently I try to put it into a temp array but not really sure what to do next. Any help would be appreciated .
I think you should begin from the end of the array to the beginning.
When you encounter a digit you have to sum this first digit, then iterate the next character and if it is a digit you have to sum digit x10, the next one: digit x100...
When the next character is not a digit, reset the multiplier and count++.
if(isDigit){
sum+= digit*multiplier;
multiplier*=10;
}
else {
multiplier=1;
}
I don't inlcude the count part.
Use the following code snippet to extract the individual words of the string, check if the extracted word is a number, and, if a number, calculate the sum and increase the count.
int i;
int j;
int count;
int sum;
char *chars;
char *pChar;
int len;
int isNum;
for (i=0, count=0, sum=0; i<strlen(array);) {
pChar = strchr(array[i], ' ');
len = pChar - array[i];
chars = malloc(sizeof(char) * (len + 1));
strncpy(chars, array[i], len);
chars[len] = '\0';
for (j =0, isNum = 1; j<len; j++) {
if (!isdigit(chars[j])) {
isNum=0;
break;
}
}
if (isNum) {
count++;
sum += atoi(chars);
}
free(chars);
if (strchr(array[pChar - array + 1] != NULL)
i = (pChar - array) + 1;
else
break;
}
If you only need the sum, you do not have to save it as char in a char array (in your code it is named tmp[]), but you can directly convert it to integer (assuming no overflow will occur).
Every time you detect the first numeric character, you should reset the tmp to zero and continue multiplying by 10 before adding the next numeric value, until you detect a non-numeric character.
edit: I think the function should return the sum. So, you'd better change the function decoration as well (and add a return sum line).
int num_count(char array[]) {
int i = 0;
int sum = 0;
int tmp;
while (array[i] != '\0') {
if (array[i] >= '0' && array[i] <= '9') {
tmp = 0;
while (array[i] >= '0' && array[i] <= '9') {
tmp = tmp * 10 + array[i] - '0';
i++;
}
sum += tmp;
}
else {
i++;
}
}
return sum;
}
I'm learning C with the CS50 course problem set 2, using the crypt function to brute force guess a password. Currently writing a function that prints all possible strings of a certain length, eg:
aa
ab
...
az
ba
...
zy
zz
I've written a fairly simple recursive function to do so:
#include <cs50.h>
#include <stdio.h>
#include <crypt.h>
#include <string.h>
void stringcycler(int n, int passLength, char *pass)
// Scrolls through all lowercase letter combinations for a string of length passLength
// Expects an integer value of the length of the strng as both n and passLength
// Also expects a char* array of length passLength with all chars set to 'a' (and a null character)
{
if(n != 0)
{
for(pass[passLength - n] = 'a'; pass[passLength - n] < 'z'; pass[passLength - n]++)
{
stringcycler(n-1, passLength, pass);
printf("%s\n", pass);
// return 0;
}
}
}
int main()
{
// Initialise char *c, and scroll through letters
int passLength = 2; // The number of characters you want to brute force guess
char pass[passLength + 1]; // Add 1 for the null character
int i;
for(i = 0; i < passLength; i++) pass[i] = 'a'; // Set every char in pass to 'a'
pass[passLength] = '\0'; // Set null character at the end of string
stringcycler(passLength, passLength, pass);
return 0;
}
It works for the most part, but only goes to yz. Whenever it sees a z it basically skips, so it goes to yz, then never does za to zz. If I add an = to the for loop line:
pass[passLength - n] < 'z';
ie.
pass[passLength - n] <= 'z';
Then it prints '{' characters in the mix. Any help? And another question is, how can I change this to work for all combos of upper and lower case too, is there a neat way of doing it?
You print after you return from you recursion, but you should print when the recursion has reached the end (or beginning, in your case) of the string. In other words, printing should be an alternative branch to recursing:
void stringcycler(int n, int len, char *pass)
{
if (n != 0) {
for (pass[len - n] = 'a'; pass[len - n] <= 'z'; pass[len - n]++) {
stringcycler(n - 1, len, pass);
}
} else {
printf("%s ", pass);
}
}
The if part constructs the strings as it recurses further down. The else part does something with the constructed string. (Of course, you must include 'z' in your loop. Your original code only prints the z in the last place, because it prints after ther recursion returns, which means thet the char buffer is in a condition that wouldn't (re-)enter the loop.)
Below is a generic backtracking algorithm for generating the password. The idea here is to imagine filling the slots for a given char array a. We will be generating the possible candidates for the given position k for the array a. I have taken the candidates as lower case ascii letters a-z and upper case ASCII letters A-Z. If you want to include other ASCII characters, just modify the construct_candidates function accordingly.
Once the array is filled i.e. k becomes PASS_LEN, we know we have generated the password, we can process it however we like, I have just printed the password here.
The value of PASS_LEN macro can be adjusted to generate password of any desired length.
#include <stdio.h>
#include <stdlib.h>
#define PASS_LEN 2
static char* construct_candidates (char a[], int k, int *count)
{
/* Lower case ASCII */
int min1 = 97;
int max1 = 122;
/* Upper case ASCII */
int min2 = 65;
int max2 = 90;
*count = (max1 - min1 + 1) + (max2 - min2 + 1);
char *cand = calloc(*count, sizeof(char));
if (cand == NULL) {
printf("malloc failed\n");
return NULL;
}
int idx = 0;
for (int i = min1; i <= max1; i++) {
cand[idx] = i;
idx++;
}
for (int i = min2; i <= max2; i++) {
cand[idx] = i;
idx++;
}
return cand;
}
static void backtrack(char a[], int k)
{
int i;
if (k == PASS_LEN) {
for (i = 0; i < PASS_LEN; i++) {
printf("%c", a[i]);
}
printf("\n");
return;
}
int cand_count = 0;
char *cand = construct_candidates(a, k, &cand_count);
if (cand == NULL) {
printf("Failed to get candidates\n");
return;
}
for (i = 0; i < cand_count; i++) {
a[k] = cand[i];
backtrack(a, k + 1);
}
free(cand);
}
int main()
{
char a[PASS_LEN] = {'\0'};
backtrack(a, 0);
}
i'm a beginner and my english is not well so sorry first. im trying to sum the numbers in a string (for a14fg5pk145 it returns 14+5+145), and it doesn't work:
"Exception thrown: read access violation.
str was 0x61."
this i my code:
void main()
{
int x, i;
char* pp;
char buffer[SIZE];
printf("Please enter numbers and letters:\n");
gets(buffer);
pp = buffer;
x = SumStr(*pp);
printf("%d", x);
}
int SumStr(char* str)
{
int sum=0, num=0, flag = 0;
while ((*str) != '\0')
{
while (((*str) > '1') && ((*str) < '9'))
{
if (flag == 0)
{
num += (*str);
flag = 1;
}
else if (flag == 1)
num = num * 10 + (*str);
str++;
}
if (flag == 0)
str++;
sum += num;
num = 0;
flag = 0;
}
return sum;
}
First problem with your code which is causing Exception.
x = SumStr(*pp);
it should be
x = SumStr(pp);
Because you should pass address of the string pointer not its first character by attaching asterix.
Second Issue that will not make it work is.
num += (*str);
and
num = num * 10 + (*str);
By (*str) you are actually adding the character ascii value instead of number.
This will solve the problem by changing the ascii value to number.
num += (*str) - '0';
num = num * 10 + (*str) - '0';
This may serve your purpose
#include<stdio.h>
#include<string.h>
int main()
{
int i, sum = 0, store;
char str[] = "a14fg5pk145asdasdad6";
int length = strlen(str);
for(i = 0; i < length; i++) {
store = 0;
while(isdigit(str[i])) {
store = (store * 10) + (str[i] - '0');
i++;
}
sum += store;
}
printf("%d\n", sum);
return 0;
}
output :
170
Pass pp, not *pp, to the function SumStr. *pp has the type char, and the function expects char *. In fact, you do not even need pp at all, just pass the buffer as the parameter.
Also:
Never use gets(). Because it is impossible to tell without knowing the
data in advance how many characters gets() will read, and because
gets() will continue to store characters past the end of the buffer, it
is extremely dangerous to use. It has been used to break computer
security. Use fgets() instead.
I am having trouble implementing my hash function for my hash table.
I want to hash my words such that A = 1, B = 2, C = 3, and so on. The position of the letter in the word is irrelevant, since we will consider permutations of the word. Moreover, the case of the letter will be irrelevant in this problem as well, so the value of a = the value of A = 1.
And for strings, abc = 1 + 2 + 3 = 6, bc = 2 + 3 = 5, etc.
And for cases where ab = 3 and aaa = 3, I have already had a way to handle that situation. Right now I just want to get the hash value.
The problem I am having right now is that aaa is giving me 1, and ab is giving me 2.
Below is my code:
int hash(char *word)
{
int h = 1;
int i, j;
char *A;
char *a;
// an array of 26 slots for 26 uppercase letters in the alphabet
A = (char *)malloc(26 * sizeof(char));
// an array of 26 slots for 26 lowercase letters in the alphabet
a = (char *)malloc(26 * sizeof(char));
for (i = 0; i < 26; i++) {
A[i] = (char)(i + 65); // fill the array from A to Z
a[i] = (char)(i + 97); // fill the array from a to z
}
for (i = 0; i < strlen(word); i++) {
//printf("HIT\n");
for (j = 0; j < 26; j++) {
// upper and lower case have the same hash value
if (word[i] == A[j] || word[i] == a[j]) {
h = h + j; // get the hash value of the word
//printf("HIT 2\n");
break;
}
}
}
printf("H: %d\n", h);
return h;
}
I think that changing
int h = 1;
to
int h = 0;
and
h = h + j;
to
h = h + j + 1;
will fix the issue.
The one other problem is that you forgot to free the malloced memory. Also, there is no need to cast the result of malloc(and family) in C.
This
for (i = 0; i < strlen(word); i++) {
will call strlen in every iteration of the loop. This will reduce the performance of your program. Use
int len = strlen(word);
for (i = 0; i < len; i++) {
instead, which is much faster as strlen isn't called in every iteration. Lastly, sizeof(char) is 1. So you can omit it.
change h=h+j to h=h+j+1
and h=1 to h=0.
Also you should free the allocated memory so include these lines just before return:
free(A);
free(a);
However I don't understand why so complicated code was written for such a simple task.
A much simpler code can be written:
int hash(char *word)
{
int sum=0;
while(*word != '\0')
{
if(*word >='A' && *word < 'A'+26)
sum=sum+(*word -'A' + 1);
else if(*word >='a' && *word < 'a'+26)
sum=sum+(*word -'a' + 1);
else
return -1;
word++;
}
return sum;
}
Multiple issues:
You still aren't freeing the arrays you allocated
Initial value of 1 for h makes no sense
You add the index to the hash. 'A' and 'a' are at index 0, so you're adding 0 in that case (so no matter how many 'a' s you give your code will return 1)
Why a dynamic array? You know the size, it isn't going to change. You could use
char A[26];
char a[26]; // you can also add initialisation, e.g. = {'a', 'b', ...
Why an array in the first place?
So, here is the quick fix, staying close to your code.
Taking all of the above into account, you could simplify to:
int hash(char const * string) {
int h = 0;
for (; *string; ++string) {
int index = tolower(*string) - 'a' + 1;
if ((index > 0) && (index < 27)) {
h += index;
}
}
return h;
}
Live
When only hashing words with non special characters, you need to handle ignored words in the caller somehow.
char hash(char const * string, int * h) {
*h = 0;
for (; *string; ++string) {
int index = tolower(*string) - 'a' + 1;
if ((index > 0) && (index < 27)) {
*h += index;
} else {
return 0;
}
}
return 1;
}
That way you can use the return value to test if the word should be ignored.
I am writing this code to convert a hex entry into its integer equivalent. So A would be 10 and B would be 11 etc. This code acts weirdly, in that it seg. faults at random locations and including an extra newline character at times will get it to work. I am trying to debug it, just so I can understand what I am doing wrong here. Can anyone take a look and help me here ? Thanks a lot for your time.
/* Fixed working code for anyone interested */
#include <stdio.h>
#include <stdlib.h>
unsigned int hextoint(const char temp[])
{
int i;
int answer = 0;
int dec;
char hexchar[] = "aAbBcCdDeEfF" ;
for ( i=0; temp[i] != '\0'; i++ )
{
if ( temp[i] == '\0')
{
return ;
}
if (temp[i] == '0' || temp[i] == 'x' || temp[i] == 'X' )
{
printf("0");
answer = temp[i];
}
// compare each temp[i] with all contents in hexchar[]
int j;
int a = temp[i];
for ( j=0; hexchar[j] != '\0'; j++)
{
if ( temp[i] == hexchar[j] )
{
answer *= 16;
answer = answer + 10 + (j/2);
// printf("%d\n",answer );
break;
}
}
}
return answer;
}
main()
{
char *test[] =
{ "bad",
"aabbdd"
"0100",
"0x1",
"0XA",
"0X0C0BE",
"abcdef",
"123456",
"0x123456",
"deadbeef",
"zog_c"
};
int answer=0;
// Calculate the number of char's.
int numberOfChars;
numberOfChars = sizeof test /sizeof test[0];
printf("main():Number of chars = %d\n",numberOfChars);
int i;
// Go through each character and convert Hex to Integers.
for ( i = 0; i<numberOfChars;i++)
{
// Need to take the first char and then go through it and convert
it.
answer = hextoint(test[i]);
printf("%d\n",answer );
}
}
Let's take a look.
unsigned int hextoint(const char temp[])
{
int i;
int answer = 0;
char hexchar[] = "aAbBcCdDeEfF" ;
for ( i=0; temp[i] != '\0'; i++ )
{
printf("In here");
printf("%c\t",temp[i] );
}
return answer;
}
This doesn't seem to even try to do any conversion. It should always return 0, since answer is never assigned any other value. Normally, you'd do something like:
for (i=0; input[i] != '\0'; i++) {
answer *= 16;
answer += digit_value(input[i]);
}
return answer;
Where digit_value (obviously enough) returns the value of an individual digit. One way to do this is:
int digit_value(char input) {
input = tolower(input);
if (input >= '0' && input <= '9')
return input - '0';
if (input >= 'a' && input <= 'f')
return input - 'a' + 10;
return -1; // signal error.
}
Then, looking at main:
main()
{
Depending on the "implicit int" rule is generally poor practice, at least IMO. It's much better to specify the return type.
// Calculate the number of char's.
int numberOfChars;
numberOfChars = sizeof test /sizeof test[0];
This actually calculates the number of strings, not the number of chars.
for ( i = 0; i<=numberOfChars;i++)
Valid subscripts run from 0 through the number of items - 1, so this attempts to read past the end of the array (giving undefined behavior).
This'll work for any number within the unsigned int range, the nice thing is it does not use any other library functions so it is great for micro-controllers where space is tight.
unsigned int hexToInt(const char *hex)
{
unsigned int result = 0;
while (*hex)
{
if (*hex > 47 && *hex < 58)
result += (*hex - 48);
else if (*hex > 64 && *hex < 71)
result += (*hex - 55);
else if (*hex > 96 && *hex < 103)
result += (*hex - 87);
if (*++hex)
result <<= 4;
}
return result;
}
The problem is with calculating numberOfChars part. sizeof test is actually the size of the pointer, not the total length of all characters in your array, so the number returned in your code would be 1, which makes the for loop go to the second index of test (test[1]) which does not have a \0 at the end. Try using strlen for calculating numberOfChars.
This may not me the most optimal method, but it should work without problem.
unsigned int hex_to_int(const char* hex) {
unsigned int result = 0;
size_t len = strlen(hex);
for (size_t i = 0; i < len; ++i) {
char cur_char = tolower(hex[len - i - 1]);
// direct return if encounter any non-hex character.
if (!(isdigit(cur_char) && (cur_char >= 'a' && cur_char <= 'f'));)
return result;
unsigned int char_val = (isdigit(cur_char) ? cur_char - '0' : 10 + cur_char - 'a');
result += round(pow(16, i)) * char_val;
}
return result;
}